Browse Source

[dnspython] Adding dnspython to local install ext-py3 (#3719)

* [dnspython] Adding dnspython to local install ext-py3

* [dnspython] Support for python3.10
Ayush Goyal 1 year ago
parent
commit
67828f395e
100 changed files with 11754 additions and 6 deletions
  1. 7 6
      desktop/core/base_requirements.txt
  2. 3 0
      desktop/core/ext-py3/dnspython-1.16.0/.coverage.ini
  3. 12 0
      desktop/core/ext-py3/dnspython-1.16.0/.gitignore
  4. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/.travis.yml
  5. 35 0
      desktop/core/ext-py3/dnspython-1.16.0/LICENSE
  6. 3 0
      desktop/core/ext-py3/dnspython-1.16.0/MANIFEST.in
  7. 78 0
      desktop/core/ext-py3/dnspython-1.16.0/Makefile
  8. 665 0
      desktop/core/ext-py3/dnspython-1.16.0/README.md
  9. 56 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/__init__.py
  10. 59 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/_compat.py
  11. 519 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/dnssec.py
  12. 19 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/dnssec.pyi
  13. 105 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/e164.py
  14. 10 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/e164.pyi
  15. 269 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/edns.py
  16. 148 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/entropy.py
  17. 10 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/entropy.pyi
  18. 128 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/exception.py
  19. 9 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/exception.pyi
  20. 130 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/flags.py
  21. 69 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/grange.py
  22. 37 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/hash.py
  23. 124 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/inet.py
  24. 4 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/inet.pyi
  25. 63 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/ipv4.py
  26. 181 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/ipv6.py
  27. 1175 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/message.py
  28. 55 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/message.pyi
  29. 994 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/name.py
  30. 35 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/name.pyi
  31. 108 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/namedict.py
  32. 182 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/node.py
  33. 17 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/node.pyi
  34. 119 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/opcode.py
  35. 0 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/py.typed
  36. 683 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/query.py
  37. 15 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/query.pyi
  38. 144 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rcode.py
  39. 456 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdata.py
  40. 17 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdata.pyi
  41. 122 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdataclass.py
  42. 347 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdataset.py
  43. 58 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdataset.pyi
  44. 287 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdatatype.py
  45. 55 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/AFSDB.py
  46. 25 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/AVC.py
  47. 75 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CAA.py
  48. 27 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CDNSKEY.py
  49. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CDS.py
  50. 123 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CERT.py
  51. 27 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CNAME.py
  52. 126 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CSYNC.py
  53. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DLV.py
  54. 26 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DNAME.py
  55. 27 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DNSKEY.py
  56. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DS.py
  57. 29 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/EUI48.py
  58. 29 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/EUI64.py
  59. 162 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/GPOS.py
  60. 86 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/HINFO.py
  61. 115 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/HIP.py
  62. 99 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/ISDN.py
  63. 327 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/LOC.py
  64. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/MX.py
  65. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NS.py
  66. 128 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC.py
  67. 196 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC3.py
  68. 90 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC3PARAM.py
  69. 60 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/OPENPGPKEY.py
  70. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/PTR.py
  71. 82 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RP.py
  72. 158 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RRSIG.py
  73. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RT.py
  74. 116 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SOA.py
  75. 25 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SPF.py
  76. 79 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SSHFP.py
  77. 84 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/TLSA.py
  78. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/TXT.py
  79. 82 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/URI.py
  80. 66 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/X25.py
  81. 57 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/__init__.py
  82. 70 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/CH/A.py
  83. 22 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/CH/__init__.py
  84. 54 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/A.py
  85. 55 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/AAAA.py
  86. 165 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/APL.py
  87. 61 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/DHCID.py
  88. 150 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/IPSECKEY.py
  89. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/KX.py
  90. 127 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NAPTR.py
  91. 60 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NSAP.py
  92. 23 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NSAP_PTR.py
  93. 89 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/PX.py
  94. 83 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/SRV.py
  95. 107 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/WKS.py
  96. 33 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/__init__.py
  97. 27 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/__init__.py
  98. 138 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dnskeybase.py
  99. 37 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dnskeybase.pyi
  100. 85 0
      desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dsbase.py

+ 7 - 6
desktop/core/base_requirements.txt

@@ -73,9 +73,10 @@ git+https://github.com/gethue/django-babel.git
 django-utils-six==2.0
 six==1.16.0
 psutil==5.8.0
--e file://${ROOT}/desktop/core/ext-py3/requests-2.27.1
--e file://${ROOT}/desktop/core/ext-py3/boto-2.49.0
--e file://${ROOT}/desktop/core/ext-py3/django-axes-5.13.0
--e file://${ROOT}/desktop/core/ext-py3/djangosaml2-0.18.0
--e file://${ROOT}/desktop/core/ext-py3/eventlet-0.30.2
--e file://${ROOT}/desktop/core/ext-py3/pysaml2-7.3.1
+-e file:///${ROOT}/desktop/core/ext-py3/requests-2.27.1
+-e file:///${ROOT}/desktop/core/ext-py3/boto-2.49.0
+-e file:///${ROOT}/desktop/core/ext-py3/django-axes-5.13.0
+-e file:///${ROOT}/desktop/core/ext-py3/djangosaml2-0.18.0
+-e file:///${ROOT}/desktop/core/ext-py3/dnspython-1.16.0  # Required for eventlet==0.30.2 and python3.10
+-e file:///${ROOT}/desktop/core/ext-py3/eventlet-0.30.2
+-e file:///${ROOT}/desktop/core/ext-py3/pysaml2-7.3.1

+ 3 - 0
desktop/core/ext-py3/dnspython-1.16.0/.coverage.ini

@@ -0,0 +1,3 @@
+[run]
+branch = True
+source = dns

+ 12 - 0
desktop/core/ext-py3/dnspython-1.16.0/.gitignore

@@ -0,0 +1,12 @@
+build
+dist
+MANIFEST
+html
+html.zip
+html.tar.gz
+tests/*.out
+*.pyc
+.coverage
+.tox
+dnspython.egg-info/
+.eggs/

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/.travis.yml

@@ -0,0 +1,23 @@
+language: python
+python:
+  - "2.7"
+  - "3.4"
+  - "3.5"
+  - "3.6"
+  #- "nightly"
+matrix:
+  include:
+    - python: 3.7  # https://github.com/travis-ci/travis-ci/issues/9815
+      dist: xenial
+      sudo: true
+  allow_failures:
+    - python: nightly
+branches:
+  except:
+    - python3
+install:
+ - pip install typing pylint pycryptodome ecdsa idna
+script:
+ - make typecheck
+ - make lint
+ - make test

+ 35 - 0
desktop/core/ext-py3/dnspython-1.16.0/LICENSE

@@ -0,0 +1,35 @@
+ISC License
+
+Copyright (C) Dnspython Contributors
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted, provided that the
+above copyright notice and this permission notice appear in all
+copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+
+Copyright (C) 2001-2017 Nominum, Inc.
+Copyright (C) Google Inc.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose with or without fee is hereby granted,
+provided that the above copyright notice and this permission notice
+appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 3 - 0
desktop/core/ext-py3/dnspython-1.16.0/MANIFEST.in

@@ -0,0 +1,3 @@
+include LICENSE ChangeLog README.md
+recursive-include examples *.txt *.py
+recursive-include tests *.txt *.py Makefile *.good example

+ 78 - 0
desktop/core/ext-py3/dnspython-1.16.0/Makefile

@@ -0,0 +1,78 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile,v 1.16 2004/03/19 00:17:27 halley Exp $
+
+PYTHON=python
+PYTHON3=python3
+
+all:
+	${PYTHON} ./setup.py build
+
+install:
+	${PYTHON} ./setup.py install
+
+clean:
+	${PYTHON} ./setup.py clean --all
+	find . -name '*.pyc' -exec rm {} \;
+	find . -name '*.pyo' -exec rm {} \;
+	rm -f TAGS
+
+distclean: clean docclean
+	rm -rf build dist
+	rm -f MANIFEST
+
+doco:
+	epydoc -v -n dnspython -u http://www.dnspython.org \
+		dns/*.py dns/rdtypes/*.py dns/rdtypes/ANY/*.py \
+		dns/rdtypes/CH/*.py \
+		dns/rdtypes/IN/*.py
+
+dockits: doco
+	mv html dnspython-html
+	tar czf html.tar.gz dnspython-html
+	zip -r html.zip dnspython-html
+	mv dnspython-html html
+
+docclean:
+	rm -rf html.tar.gz html.zip html
+
+kits:
+	${PYTHON3} ./setup.py sdist --formats=gztar,zip bdist_wheel
+
+tags:
+	find . -name '*.py' -print | etags -
+
+check: test
+
+test:
+	cd tests; make PYTHON=${PYTHON} test
+
+test2:
+	cd tests; make PYTHON=python test
+
+test3:
+	cd tests; make PYTHON=${PYTHON3} test
+
+lint:
+	pylint dns tests examples/*.py
+
+lint3:
+	pylint3 dns tests examples/*.py
+
+typecheck:
+	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

+ 665 - 0
desktop/core/ext-py3/dnspython-1.16.0/README.md

@@ -0,0 +1,665 @@
+# dnspython
+
+[![Build Status](https://travis-ci.org/rthalley/dnspython.svg?branch=master)](https://travis-ci.org/rthalley/dnspython)
+
+## INTRODUCTION
+
+dnspython is a DNS toolkit for Python. It supports almost all record types. It
+can be used for queries, zone transfers, and dynamic updates. It supports TSIG
+authenticated messages and EDNS0.
+
+dnspython provides both high and low level access to DNS. The high level classes
+perform queries for data of a given name, type, and class, and return an answer
+set. The low level classes allow direct manipulation of DNS zones, messages,
+names, and records.
+
+To see a few of the ways dnspython can be used, look in the `examples/` directory.
+
+dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For
+simple forward DNS lookups, it's better to use `socket.gethostbyname()`.
+
+dnspython originated at Nominum where it was developed
+to facilitate the testing of DNS software.
+
+## INSTALLATION
+
+* Many distributions have dnspython packaged for you, so you should
+  check there first.
+* If you have pip installed, you can do `pip install dnspython`
+* If not just download the source file and unzip it, then run
+  `sudo python setup.py install`
+
+## ABOUT THIS RELEASE
+
+This is dnspython 1.16.0
+
+### Notices
+
+Python 2.x support ends with the release of 1.16.0, unless there are
+critical bugs in 1.16.0.  Future versions of dnspython will only
+support Python 3.
+
+Version numbering of future dnspython releases will also start at 2.0, as
+incompatible changes will be permitted.  We're not planning huge changes at
+this time, but we'd like to do a better job at IDNA, and there are other
+API improvements to be made.
+
+The ChangeLog has been discontinued.  Please see the git history for detailed
+change information.
+
+### New since 1.15.0:
+
+* Much of the internals of dns.query.udp() and dns.query.tcp() have
+  been factored out into dns.query.send_udp(),
+  dns.query.receive_udp(), dns.query.send_tcp(), and
+  dns.query.receive_tcp().  Applications which want more control over
+  the socket may find the new routines helpful; for example it would
+  be easy to send multiple queries over a single TCP connection.
+  
+* The OPENPGPKEY RR, and the CHAOS class A RR are now supported.
+
+* EDNS0 client-subnet is supported.
+
+* dns.resover.query() now has a lifetime timeout optional parameter.
+
+* pycryptodome and pycryptodomex are now supported and recommended for use
+  instead of pycrypto.
+  
+* dns.message.from_wire() now has an ignore_trailing option.
+
+* type signatures have been provided.
+
+* module dns.hash is now deprecated, use standard Python libraries instead.
+
+* setup.py supports Cythonization to improve performance.
+
+### Bugs fixed since 1.15.0:
+
+* DNSSEC signature validation didn't check names correctly.  [Issue #295]
+
+* The NXDOMAIN exception should not use its docstring.  [Issue #253]
+
+* Fixed regression where trailing zeros in APL RRs were not
+  suppressed, and then fixed the problem where trailing zeros
+  were not added back properly on python 3 when needed.
+  
+* Masterfile TTL defaulting is now harmonized with BIND practice.
+
+* dns.query.xfr() now raises on a non-zero rcode.
+
+* Rdata module importing is now locked to avoid races.
+
+* Several Python 3 incompatibilities have been fixed.
+
+* NSEC3 bitmap parsing now works with mulitple NSEC3 windows.
+
+* dns.renderer.Render supports TSIG on DNS envelope sequences.
+
+* DNSSEC validation now checks names properly [Issue #295]
+
+### New since 1.14.0:
+
+* IDNA 2008 support is now available if the "idna" module has been
+  installed and IDNA 2008 is requested.  The default IDNA behavior is
+  still IDNA 2003.  The new IDNA codec mechanism is currently only
+  useful for direct calls to dns.name.from_text() or
+  dns.name.from_unicode(), but in future releases it will be deployed
+  throughout dnspython, e.g. so that you can read a masterfile with an
+  IDNA 2008 codec in force.
+
+* By default, dns.name.to_unicode() is not strict about which
+  version of IDNA the input complies with.  Strictness can be
+  requested by using one of the strict IDNA codecs.
+
+* The AVC RR is now supported.
+
+### Bugs fixed since 1.14.0:
+
+* Some problems with newlines in various output modes have been
+  addressed.
+
+* dns.name.to_text() now returns text and not bytes on Python 3.x
+
+* Miscellaneous fixes for the Python 2/3 codeline merge.
+
+* Many "lint" fixes after the addition of pylint support.
+
+* The random number generator reseeds after a fork().
+
+
+## REQUIREMENTS
+
+Python 2.7 or 3.4+.
+
+
+## HOME PAGE
+
+For the latest in releases, documentation, and information, visit the dnspython
+home page at http://www.dnspython.org/
+
+
+## BUG REPORTS
+
+Bug reports may be opened at
+https://github.com/rthalley/dnspython/issues or sent to
+bugs@dnspython.org
+
+
+## MAILING LISTS
+
+A number of mailing lists are available. Visit the dnspython home page to
+subscribe or unsubscribe.
+
+
+## PRIOR RELEASE INFORMATION
+
+### New since 1.13.0:
+
+* CSYNC RRs are now supported.
+
+* `dns/message.py` (`make_query`): Setting any value which implies EDNS will
+  turn on EDNS if `use_edns` has not been specified.
+
+### Bugs fixed since 1.13.0:
+
+* TSIG signature algorithm setting was broken by the Python 2 and Python 3 code
+  line merge.
+
+* A bug in the LOC RR destroyed N/S and E/W distinctions within a degree of the
+  equator or prime merdian respectively.
+
+* Misc. fixes to deal with fallout from the Python 2 & 3 merge.
+  Fixes #156, #157, #158, #159, #160.
+
+* Running with python optimization on caused issues when stripped docstrings
+  were referenced. Fixes #154
+
+* `dns.zone.from_text()` erroneously required the zone to be provided.
+  Fixes #153
+
+### New since 1.12.0:
+
+* Dnspython now uses a single source for Python 2 and Python 3, eliminating the
+  painful merging between the Python 2 and Python 3 branches. Thank you so much
+  to Arthur Gautier for taking on this challenge and making it work! It was a
+  big job!
+
+* Support for Python older than 2.6 dropped.
+
+* Support for Python older than 3.3 dropped.
+
+* Zone origin can be specified as a string.
+
+* A rich string representation for all DNSExceptions.
+
+* setuptools has replaced distutils
+
+* Added support for CAA, CDS, CDNSKEY, EUI48, EUI64, and URI RR types.
+
+* Names now support the pickle protocol.
+
+* Ports can be specified per-nameserver in the stub resolver.
+
+### Bugs fixed since 1.12.0:
+
+* A number of Unicode name bugs have been fixed.
+
+* `resolv.conf` processing now rejects lines with too few tokens.
+
+* NameDicts now keep the max-depth value correct, and update properly.
+
+### New since 1.11.1:
+
+* Added `dns.zone.to_text()`.
+
+* Added support for "options rotate" in `/etc/resolv.conf`.
+
+* `dns.rdtypes.ANY.DNSKEY` now has helpers functions to convert between the
+  numeric form of the flags and a set of human-friendly strings
+
+* The reverse name of an IPv6 mapped IPv4 address is now in the IPv4 reverse
+  namespace.
+
+* The test system can now run the tests without requiring dnspython to be
+  installed.
+
+* Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module)
+
+### Bugs fixed since 1.11.1:
+
+* dnspython raised an exception when reading a masterfile starting with leading
+  whitespace
+
+* dnspython was affected by a python slicing API bug present on 64-bit windows.
+
+* Unicode escaping was applied at the wrong time.
+
+* RRSIG `to_text()` did not respect the relativize setting.
+
+* APL RRs with zero rdlength were rejected.
+
+* The tokenizer could put back an unescaped token.
+
+* Making a response to a message signed with TSIG was broken.
+
+* The IXFR state machine didn't handle long IXFR diffs.
+
+### New since 1.11.0:
+
+* Nothing
+
+### Bugs fixed since 1.11.0:
+
+* `dns.resolver.Resolver` erroneously referred to `retry_servfail`
+  instead of `self.retry_servfail`.
+
+* `dns.tsigkeyring.to_text()` would fail trying to convert the keyname to text.
+
+* Multi-message TSIGs were broken for algorithms other than HMAC-MD5 because we
+  weren't passing the right digest module to the HMAC code.
+
+* `dns.dnssec._find_candidate_keys()` tried to extract the key from the wrong
+  variable name.
+
+* $GENERATE tests were not backward compatible with python 2.4.
+
+### New since 1.10.0:
+
+* $GENERATE support
+
+* TLSA RR support
+
+* Added set_flags() method to dns.resolver.Resolver
+
+### Bugs fixed since 1.10.0:
+
+* Names with offsets >= 2^14 are no longer added to the compression table.
+
+* The "::" syntax is not used to shorten a single 16-bit section of the text
+  form an IPv6 address.
+
+* Caches are now locked.
+
+* YXDOMAIN is raised if seen by the resolver.
+
+* Empty rdatasets are not printed.
+
+* DNSKEY key tags are no longer assumed to be unique.
+
+### New since 1.9.4:
+
+* Added dns.resolver.LRUCache. In this cache implementation, the cache size is
+  limited to a user-specified number of nodes, and when adding a new node to a
+  full cache the least-recently used node is removed. If you're crawling the web
+  or otherwise doing lots of resolutions and you are using a cache, switching
+  to the LRUCache is recommended.
+
+* `dns.resolver.query()` will try TCP if a UDP response is truncated.
+
+* The python socket module's DNS methods can be now be overridden with
+  implementations that use dnspython's resolver.
+
+* Old DNSSEC types KEY, NXT, and SIG have been removed.
+
+* Whitespace is allowed in SSHFP fingerprints.
+
+* Origin checking in `dns.zone.from_xfr()` can be disabled.
+
+* Trailing junk checking can be disabled.
+
+* A source port can be specified when creating a resolver query.
+
+* All EDNS values may now be specified to `dns.message.make_query()`.
+
+### Bugs fixed since 1.9.4:
+
+* IPv4 and IPv6 address processing is now stricter.
+
+* Bounds checking of slices in rdata wire processing is now more strict, and
+  bounds errors (e.g. we got less data than was expected) now raise
+  `dns.exception.FormError` rather than `IndexError`.
+
+* Specifying a source port without specifying source used to have no effect, but
+  now uses the wildcard address and the specified port.
+
+### New since 1.9.3:
+
+* Nothing.
+
+### Bugs fixed since 1.9.3:
+
+* The rdata `_wire_cmp()` routine now handles relative names.
+
+* The SIG RR implementation was missing `import struct`.
+
+### New since 1.9.2:
+
+* A boolean parameter, `raise_on_no_answer`, has been added to the `query()`
+  methods. In no-error, no-data situations, this parameter determines whether
+  `NoAnswer` should be raised or not. If True, `NoAnswer` is raised. If False,
+  then an `Answer()` object with a None rrset will be returned.
+
+* Resolver `Answer()` objects now have a canonical_name field.
+
+* Rdata now has a `__hash__` method.
+
+### Bugs fixed since 1.9.2:
+
+* Dnspython was erroneously doing case-insensitive comparisons of the names in
+  NSEC and RRSIG RRs.
+
+* We now use `is` and not `==` when testing what section an RR is in.
+
+* The resolver now disallows metaqueries.
+
+### New since 1.9.1:
+
+* Nothing.
+
+### Bugs fixed since 1.9.1:
+
+* The `dns.dnssec` module didn't work at all due to missing imports that escaped
+  detection in testing because the test suite also did the imports. The third
+  time is the charm!
+
+### New since 1.9.0:
+
+* Nothing.
+
+### Bugs fixed since 1.9.0:
+
+* The `dns.dnssec` module didn't work with DSA due to namespace contamination
+  from a "from"-style import.
+
+### New since 1.8.0:
+
+* dnspython now uses `poll()` instead of `select()` when available.
+
+* Basic DNSSEC validation can be done using `dns.dnsec.validate()` and
+  `dns.dnssec.validate_rrsig()` if you have PyCrypto 2.3 or later installed.
+  Complete secure resolution is not yet available.
+
+* Added `key_id()` to the DNSSEC module, which computes the DNSSEC key id of a
+  DNSKEY rdata.
+
+* Added `make_ds()` to the DNSSEC module, which returns the DS RR for a given
+  DNSKEY rdata.
+
+* dnspython now raises an exception if HMAC-SHA284 or HMAC-SHA512 are used with
+  a Python older than 2.5.2. (Older Pythons do not compute the correct value.)
+
+* Symbolic constants are now available for TSIG algorithm names.
+
+### Bugs fixed since 1.8.0
+
+* `dns.resolver.zone_for_name()` didn't handle a query response with a CNAME or
+  DNAME correctly in some cases.
+
+* When specifying rdata types and classes as text, Unicode strings may now be
+  used.
+
+* Hashlib compatibility issues have been fixed.
+
+* `dns.message` now imports `dns.edns`.
+
+* The TSIG algorithm value was passed incorrectly to `use_tsig()` in some cases.
+
+### New since 1.7.1:
+
+* Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 and hmac-sha512
+  has been contributed by Kevin Chen.
+
+* The tokenizer's tokens are now Token objects instead of (type, value) tuples.
+
+### Bugs fixed since 1.7.1:
+
+* Escapes in masterfiles now work correctly. Previously they were only working
+  correctly when the text involved was part of a domain name.
+
+* When constructing a DDNS update, if the `present()` method was used with a
+  single rdata, a zero TTL was not added.
+
+* The entropy pool needed locking to be thread safe.
+
+* The entropy pool's reading of `/dev/random` could cause dnspython to block.
+
+* The entropy pool did buffered reads, potentially consuming more randomness
+  than we needed.
+
+* The entropy pool did not seed with high quality randomness on Windows.
+
+* SRV records were compared incorrectly.
+
+* In the e164 query function, the resolver parameter was not used.
+
+### New since 1.7.0:
+
+* Nothing
+
+### Bugs fixed since 1.7.0:
+
+* The 1.7.0 kitting process inadvertently omitted the code for the DLV RR.
+
+* Negative DDNS prerequisites are now handled correctly.
+
+### New since 1.6.0:
+
+* Rdatas now have a `to_digestable()` method, which returns the DNSSEC canonical
+  form of the rdata, suitable for use in signature computations.
+
+* The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported.
+
+* An entropy module has been added and is used to randomize query ids.
+
+* EDNS0 options are now supported.
+
+* UDP IXFR is now supported.
+
+* The wire format parser now has a `one_rr_per_rrset` mode, which suppresses the
+  usual coalescing of all RRs of a given type into a single RRset.
+
+* Various helpful DNSSEC-related constants are now defined.
+
+* The resolver's `query()` method now has an optional `source` parameter,
+  allowing the source IP address to be specified.
+
+### Bugs fixed since 1.6.0:
+
+* On Windows, the resolver set the domain incorrectly.
+
+* DS RR parsing only allowed one Base64 chunk.
+
+* TSIG validation didn't always use absolute names.
+
+* `NSEC.to_text()` only printed the last window.
+
+* We did not canonicalize IPv6 addresses before comparing them; we
+  would thus treat equivalent but different textual forms, e.g.
+  "1:00::1" and "1::1" as being non-equivalent.
+
+* If the peer set a TSIG error, we didn't raise an exception.
+
+* Some EDNS bugs in the message code have been fixed (see the ChangeLog
+  for details).
+
+### New since 1.5.0:
+
+* Added dns.inet.is_multicast().
+
+### Bugs fixed since 1.5.0:
+
+* If `select()` raises an exception due to EINTR, we should just `select()`
+  again.
+
+* If the queried address is a multicast address, then don't check that the
+  address of the response is the same as the address queried.
+
+* NAPTR comparisons didn't compare the preference field due to a typo.
+
+* Testing of whether a Windows NIC is enabled now works on Vista thanks to code
+  contributed by Paul Marks.
+
+### New since 1.4.0:
+
+* Answer objects now support more of the python sequence protocol, forwarding
+  the requests to the answer rrset. E.g. `for a in answer` is equivalent to
+  `for a in  answer.rrset`, `answer[i]` is equivalent to `answer.rrset[i]`, and
+  `answer[i:j]` is equivalent to `answer.rrset[i:j]`.
+
+* Making requests using EDNS, including indicating DNSSEC awareness,
+  is now easier. For example, you can now say:
+  `q = dns.message.make_query('www.dnspython.org', 'MX', want_dnssec=True)`
+
+* `dns.query.xfr()` can now be used for IXFR.
+
+* Support has been added for the DHCID, IPSECKEY, and SPF RR types.
+
+* UDP messages from unexpected sources can now be ignored by setting
+  `ignore_unexpected` to True when calling `dns.query.udp`.
+
+### Bugs fixed since 1.4.0:
+
+* If `/etc/resolv.conf` didn't exist, we raised an exception instead of simply
+  using the default resolver configuration.
+
+* In `dns.resolver.Resolver._config_win32_fromkey()`, we were passing the wrong
+  variable to `self._config_win32_search()`.
+
+### New since 1.3.5:
+
+* You can now convert E.164 numbers to/from their ENUM name forms:
+  ```python
+  >>> import dns.e164
+  >>> n = dns.e164.from_e164("+1 555 1212")
+  >>> n
+  <DNS name 2.1.2.1.5.5.5.1.e164.arpa.>
+  >>> dns.e164.to_e164(n)
+  '+15551212'
+  ```
+
+* You can now convert IPv4 and IPv6 address to/from their corresponding DNS
+  reverse map names:
+  ```python
+  >>> import dns.reversename
+  >>> n = dns.reversename.from_address("127.0.0.1")
+  >>> n
+  <DNS name 1.0.0.127.in-addr.arpa.>
+  >>> dns.reversename.to_address(n)
+  '127.0.0.1'
+  ```
+
+* You can now convert between Unicode strings and their IDN ACE form:
+  ```python
+  >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.')
+  >>> n
+  <DNS name xn--les-lves-50ai.example.>
+  >>> n.to_unicode()
+  u'les-\xe9l\xe8ves.example.'
+  ```
+
+* The origin parameter to `dns.zone.from_text()` and `dns.zone.to_text()` is now
+  optional. If not specified, the origin will be taken from the first $ORIGIN
+  statement in the master file.
+
+* Sanity checking of a zone can be disabled; this is useful when working with
+  files which are zone fragments.
+
+### Bugs fixed since 1.3.5:
+
+* The correct delimiter was not used when retrieving the list of nameservers
+  from the registry in certain versions of windows.
+
+* The floating-point version of latitude and longitude in LOC RRs
+  (`float_latitude` and `float_longitude`) had incorrect signs for south
+  latitudes and west longitudes.
+
+* BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. SOA fields
+  refresh, retry, expire, and minimum; SIG/RRSIG field original_ttl).
+
+* TTLs are now bounds checked when their text form is parsed, and their values
+  must be in the closed interval `[0, 2^31 - 1]`.
+
+### New since 1.3.4:
+
+* In the resolver, if time goes backward a little bit, ignore it.
+
+* `zone_for_name()` has been added to the resolver module. It returns the zone
+  which is authoritative for the specified name, which is handy for dynamic
+  update. E.g.
+
+      import dns.resolver
+      print dns.resolver.zone_for_name('www.dnspython.org')
+
+  will output `"dnspython.org."` and
+  `print dns.resolver.zone_for_name('a.b.c.d.e.f.example.')`
+  will output `"."`.
+
+* The default resolver can be fetched with the `get_default_resolver()` method.
+
+* You can now get the parent (immediate superdomain) of a name by using the
+  `parent()` method.
+
+* `Zone.iterate_rdatasets()` and `Zone.iterate_rdatas()` now have a default
+  rdtype of `dns.rdatatype.ANY` like the documentation says.
+
+* A Dynamic DNS example, ddns.py, has been added.
+
+### New since 1.3.3:
+
+* The source address and port may now be specified when calling
+  `dns.query.{udp,tcp,xfr}`.
+
+* The resolver now does exponential backoff each time it runs through all of the
+  nameservers.
+
+* Rcodes which indicate a nameserver is likely to be a "permanent failure" for a
+  query cause the nameserver to be removed from the mix for that query.
+
+### New since 1.3.2:
+
+* `dns.message.Message.find_rrset()` now uses an index, vastly improving the
+  `from_wire()` performance of large messages such as zone transfers.
+
+* Added `dns.message.make_response()`, which creates a skeletal response for the
+  specified query.
+
+* Added `opcode()` and `set_opcode()` convenience methods to the
+  `dns.message.Message` class. Added the `request_payload` attribute to the
+  Message class.
+
+* The `file` parameter of `dns.name.Name.to_wire()` is now optional; if omitted,
+  the wire form will be returned as the value of the function.
+
+* `dns.zone.from_xfr()` in relativization mode incorrectly set `zone.origin` to
+  the empty name.
+
+* The masterfile parser incorrectly rejected TXT records where a value was not
+  quoted.
+
+### New since 1.3.1:
+
+* The NSEC format doesn't allow specifying types by number, so we shouldn't
+  either. (Using the unknown type format is still OK though.)
+
+* The resolver wasn't catching `dns.exception.Timeout`, so a timeout erroneously
+  caused the whole resolution to fail instead of just going on to the next
+  server.
+
+* The renderer module didn't import random, causing an exception to be raised if
+  a query id wasn't provided when a Renderer was created.
+
+* The conversion of LOC milliseconds values from text to binary was incorrect if
+  the length of the milliseconds string was not 3.
+
+### New since 1.3.0:
+
+* Added support for the SSHFP type.
+
+### New since 1.2.0:
+
+* Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY.
+
+* This release fixes all known bugs.
+
+* See the ChangeLog file for more detailed information on changes since the
+  prior release.

+ 56 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/__init__.py

@@ -0,0 +1,56 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""dnspython DNS toolkit"""
+
+__all__ = [
+    'dnssec',
+    'e164',
+    'edns',
+    'entropy',
+    'exception',
+    'flags',
+    'hash',
+    'inet',
+    'ipv4',
+    'ipv6',
+    'message',
+    'name',
+    'namedict',
+    'node',
+    'opcode',
+    'query',
+    'rcode',
+    'rdata',
+    'rdataclass',
+    'rdataset',
+    'rdatatype',
+    'renderer',
+    'resolver',
+    'reversename',
+    'rrset',
+    'set',
+    'tokenizer',
+    'tsig',
+    'tsigkeyring',
+    'ttl',
+    'rdtypes',
+    'update',
+    'version',
+    'wiredata',
+    'zone',
+]

+ 59 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/_compat.py

@@ -0,0 +1,59 @@
+import sys
+import decimal
+from decimal import Context
+
+PY3 = sys.version_info[0] == 3
+PY2 = sys.version_info[0] == 2
+
+
+if PY3:
+    long = int
+    xrange = range
+else:
+    long = long  # pylint: disable=long-builtin
+    xrange = xrange  # pylint: disable=xrange-builtin
+
+# unicode / binary types
+if PY3:
+    text_type = str
+    binary_type = bytes
+    string_types = (str,)
+    unichr = chr
+    def maybe_decode(x):
+        return x.decode()
+    def maybe_encode(x):
+        return x.encode()
+    def maybe_chr(x):
+        return x
+    def maybe_ord(x):
+        return x
+else:
+    text_type = unicode  # pylint: disable=unicode-builtin, undefined-variable
+    binary_type = str
+    string_types = (
+        basestring,  # pylint: disable=basestring-builtin, undefined-variable
+    )
+    unichr = unichr  # pylint: disable=unichr-builtin
+    def maybe_decode(x):
+        return x
+    def maybe_encode(x):
+        return x
+    def maybe_chr(x):
+        return chr(x)
+    def maybe_ord(x):
+        return ord(x)
+
+
+def round_py2_compat(what):
+    """
+    Python 2 and Python 3 use different rounding strategies in round(). This
+    function ensures that results are python2/3 compatible and backward
+    compatible with previous py2 releases
+    :param what: float
+    :return: rounded long
+    """
+    d = Context(
+        prec=len(str(long(what))),  # round to integer with max precision
+        rounding=decimal.ROUND_HALF_UP
+    ).create_decimal(str(what))  # str(): python 2.6 compat
+    return long(d)

+ 519 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/dnssec.py

@@ -0,0 +1,519 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Common DNSSEC-related functions and constants."""
+
+from io import BytesIO
+import struct
+import time
+
+import dns.exception
+import dns.name
+import dns.node
+import dns.rdataset
+import dns.rdata
+import dns.rdatatype
+import dns.rdataclass
+from ._compat import string_types
+
+
+class UnsupportedAlgorithm(dns.exception.DNSException):
+    """The DNSSEC algorithm is not supported."""
+
+
+class ValidationFailure(dns.exception.DNSException):
+    """The DNSSEC signature is invalid."""
+
+
+#: RSAMD5
+RSAMD5 = 1
+#: DH
+DH = 2
+#: DSA
+DSA = 3
+#: ECC
+ECC = 4
+#: RSASHA1
+RSASHA1 = 5
+#: DSANSEC3SHA1
+DSANSEC3SHA1 = 6
+#: RSASHA1NSEC3SHA1
+RSASHA1NSEC3SHA1 = 7
+#: RSASHA256
+RSASHA256 = 8
+#: RSASHA512
+RSASHA512 = 10
+#: ECDSAP256SHA256
+ECDSAP256SHA256 = 13
+#: ECDSAP384SHA384
+ECDSAP384SHA384 = 14
+#: INDIRECT
+INDIRECT = 252
+#: PRIVATEDNS
+PRIVATEDNS = 253
+#: PRIVATEOID
+PRIVATEOID = 254
+
+_algorithm_by_text = {
+    'RSAMD5': RSAMD5,
+    'DH': DH,
+    'DSA': DSA,
+    'ECC': ECC,
+    'RSASHA1': RSASHA1,
+    'DSANSEC3SHA1': DSANSEC3SHA1,
+    'RSASHA1NSEC3SHA1': RSASHA1NSEC3SHA1,
+    'RSASHA256': RSASHA256,
+    'RSASHA512': RSASHA512,
+    'INDIRECT': INDIRECT,
+    'ECDSAP256SHA256': ECDSAP256SHA256,
+    'ECDSAP384SHA384': ECDSAP384SHA384,
+    'PRIVATEDNS': PRIVATEDNS,
+    'PRIVATEOID': PRIVATEOID,
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_algorithm_by_value = {y: x for x, y in _algorithm_by_text.items()}
+
+
+def algorithm_from_text(text):
+    """Convert text into a DNSSEC algorithm value.
+
+    Returns an ``int``.
+    """
+
+    value = _algorithm_by_text.get(text.upper())
+    if value is None:
+        value = int(text)
+    return value
+
+
+def algorithm_to_text(value):
+    """Convert a DNSSEC algorithm value to text
+
+    Returns a ``str``.
+    """
+
+    text = _algorithm_by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text
+
+
+def _to_rdata(record, origin):
+    s = BytesIO()
+    record.to_wire(s, origin=origin)
+    return s.getvalue()
+
+
+def key_id(key, origin=None):
+    """Return the key id (a 16-bit number) for the specified key.
+
+    Note the *origin* parameter of this function is historical and
+    is not needed.
+
+    Returns an ``int`` between 0 and 65535.
+    """
+
+    rdata = _to_rdata(key, origin)
+    rdata = bytearray(rdata)
+    if key.algorithm == RSAMD5:
+        return (rdata[-3] << 8) + rdata[-2]
+    else:
+        total = 0
+        for i in range(len(rdata) // 2):
+            total += (rdata[2 * i] << 8) + \
+                rdata[2 * i + 1]
+        if len(rdata) % 2 != 0:
+            total += rdata[len(rdata) - 1] << 8
+        total += ((total >> 16) & 0xffff)
+        return total & 0xffff
+
+
+def make_ds(name, key, algorithm, origin=None):
+    """Create a DS record for a DNSSEC key.
+
+    *name* is the owner name of the DS record.
+
+    *key* is a ``dns.rdtypes.ANY.DNSKEY``.
+
+    *algorithm* is a string describing which hash algorithm to use.  The
+    currently supported hashes are "SHA1" and "SHA256".  Case does not
+    matter for these strings.
+
+    *origin* is a ``dns.name.Name`` and will be used as the origin
+    if *key* is a relative name.
+
+    Returns a ``dns.rdtypes.ANY.DS``.
+    """
+
+    if algorithm.upper() == 'SHA1':
+        dsalg = 1
+        hash = SHA1.new()
+    elif algorithm.upper() == 'SHA256':
+        dsalg = 2
+        hash = SHA256.new()
+    else:
+        raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm)
+
+    if isinstance(name, string_types):
+        name = dns.name.from_text(name, origin)
+    hash.update(name.canonicalize().to_wire())
+    hash.update(_to_rdata(key, origin))
+    digest = hash.digest()
+
+    dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest
+    return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0,
+                               len(dsrdata))
+
+
+def _find_candidate_keys(keys, rrsig):
+    candidate_keys = []
+    value = keys.get(rrsig.signer)
+    if value is None:
+        return None
+    if isinstance(value, dns.node.Node):
+        try:
+            rdataset = value.find_rdataset(dns.rdataclass.IN,
+                                           dns.rdatatype.DNSKEY)
+        except KeyError:
+            return None
+    else:
+        rdataset = value
+    for rdata in rdataset:
+        if rdata.algorithm == rrsig.algorithm and \
+                key_id(rdata) == rrsig.key_tag:
+            candidate_keys.append(rdata)
+    return candidate_keys
+
+
+def _is_rsa(algorithm):
+    return algorithm in (RSAMD5, RSASHA1,
+                         RSASHA1NSEC3SHA1, RSASHA256,
+                         RSASHA512)
+
+
+def _is_dsa(algorithm):
+    return algorithm in (DSA, DSANSEC3SHA1)
+
+
+def _is_ecdsa(algorithm):
+    return _have_ecdsa and (algorithm in (ECDSAP256SHA256, ECDSAP384SHA384))
+
+
+def _is_md5(algorithm):
+    return algorithm == RSAMD5
+
+
+def _is_sha1(algorithm):
+    return algorithm in (DSA, RSASHA1,
+                         DSANSEC3SHA1, RSASHA1NSEC3SHA1)
+
+
+def _is_sha256(algorithm):
+    return algorithm in (RSASHA256, ECDSAP256SHA256)
+
+
+def _is_sha384(algorithm):
+    return algorithm == ECDSAP384SHA384
+
+
+def _is_sha512(algorithm):
+    return algorithm == RSASHA512
+
+
+def _make_hash(algorithm):
+    if _is_md5(algorithm):
+        return MD5.new()
+    if _is_sha1(algorithm):
+        return SHA1.new()
+    if _is_sha256(algorithm):
+        return SHA256.new()
+    if _is_sha384(algorithm):
+        return SHA384.new()
+    if _is_sha512(algorithm):
+        return SHA512.new()
+    raise ValidationFailure('unknown hash for algorithm %u' % algorithm)
+
+
+def _make_algorithm_id(algorithm):
+    if _is_md5(algorithm):
+        oid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05]
+    elif _is_sha1(algorithm):
+        oid = [0x2b, 0x0e, 0x03, 0x02, 0x1a]
+    elif _is_sha256(algorithm):
+        oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]
+    elif _is_sha512(algorithm):
+        oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03]
+    else:
+        raise ValidationFailure('unknown algorithm %u' % algorithm)
+    olen = len(oid)
+    dlen = _make_hash(algorithm).digest_size
+    idbytes = [0x30] + [8 + olen + dlen] + \
+              [0x30, olen + 4] + [0x06, olen] + oid + \
+              [0x05, 0x00] + [0x04, dlen]
+    return struct.pack('!%dB' % len(idbytes), *idbytes)
+
+
+def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
+    """Validate an RRset against a single signature rdata
+
+    The owner name of *rrsig* is assumed to be the same as the owner name
+    of *rrset*.
+
+    *rrset* is the RRset to validate.  It can be a ``dns.rrset.RRset`` or
+    a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
+
+    *rrsig* is a ``dns.rdata.Rdata``, the signature to validate.
+
+    *keys* is the key dictionary, used to find the DNSKEY associated with
+    a given name.  The dictionary is keyed by a ``dns.name.Name``, and has
+    ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
+
+    *origin* is a ``dns.name.Name``, the origin to use for relative names.
+
+    *now* is an ``int``, the time to use when validating the signatures,
+    in seconds since the UNIX epoch.  The default is the current time.
+    """
+
+    if isinstance(origin, string_types):
+        origin = dns.name.from_text(origin, dns.name.root)
+
+    candidate_keys = _find_candidate_keys(keys, rrsig)
+    if candidate_keys is None:
+        raise ValidationFailure('unknown key')
+
+    for candidate_key in candidate_keys:
+        # For convenience, allow the rrset to be specified as a (name,
+        # rdataset) tuple as well as a proper rrset
+        if isinstance(rrset, tuple):
+            rrname = rrset[0]
+            rdataset = rrset[1]
+        else:
+            rrname = rrset.name
+            rdataset = rrset
+
+        if now is None:
+            now = time.time()
+        if rrsig.expiration < now:
+            raise ValidationFailure('expired')
+        if rrsig.inception > now:
+            raise ValidationFailure('not yet valid')
+
+        hash = _make_hash(rrsig.algorithm)
+
+        if _is_rsa(rrsig.algorithm):
+            keyptr = candidate_key.key
+            (bytes_,) = struct.unpack('!B', keyptr[0:1])
+            keyptr = keyptr[1:]
+            if bytes_ == 0:
+                (bytes_,) = struct.unpack('!H', keyptr[0:2])
+                keyptr = keyptr[2:]
+            rsa_e = keyptr[0:bytes_]
+            rsa_n = keyptr[bytes_:]
+            try:
+                pubkey = CryptoRSA.construct(
+                    (number.bytes_to_long(rsa_n),
+                     number.bytes_to_long(rsa_e)))
+            except ValueError:
+                raise ValidationFailure('invalid public key')
+            sig = rrsig.signature
+        elif _is_dsa(rrsig.algorithm):
+            keyptr = candidate_key.key
+            (t,) = struct.unpack('!B', keyptr[0:1])
+            keyptr = keyptr[1:]
+            octets = 64 + t * 8
+            dsa_q = keyptr[0:20]
+            keyptr = keyptr[20:]
+            dsa_p = keyptr[0:octets]
+            keyptr = keyptr[octets:]
+            dsa_g = keyptr[0:octets]
+            keyptr = keyptr[octets:]
+            dsa_y = keyptr[0:octets]
+            pubkey = CryptoDSA.construct(
+                (number.bytes_to_long(dsa_y),
+                 number.bytes_to_long(dsa_g),
+                 number.bytes_to_long(dsa_p),
+                 number.bytes_to_long(dsa_q)))
+            sig = rrsig.signature[1:]
+        elif _is_ecdsa(rrsig.algorithm):
+            # use ecdsa for NIST-384p -- not currently supported by pycryptodome
+
+            keyptr = candidate_key.key
+
+            if rrsig.algorithm == ECDSAP256SHA256:
+                curve = ecdsa.curves.NIST256p
+                key_len = 32
+            elif rrsig.algorithm == ECDSAP384SHA384:
+                curve = ecdsa.curves.NIST384p
+                key_len = 48
+
+            x = number.bytes_to_long(keyptr[0:key_len])
+            y = number.bytes_to_long(keyptr[key_len:key_len * 2])
+            if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y):
+                raise ValidationFailure('invalid ECDSA key')
+            point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
+            verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
+                                                                      curve)
+            pubkey = ECKeyWrapper(verifying_key, key_len)
+            r = rrsig.signature[:key_len]
+            s = rrsig.signature[key_len:]
+            sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r),
+                                        number.bytes_to_long(s))
+
+        else:
+            raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
+
+        hash.update(_to_rdata(rrsig, origin)[:18])
+        hash.update(rrsig.signer.to_digestable(origin))
+
+        if rrsig.labels < len(rrname) - 1:
+            suffix = rrname.split(rrsig.labels + 1)[1]
+            rrname = dns.name.from_text('*', suffix)
+        rrnamebuf = rrname.to_digestable(origin)
+        rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
+                              rrsig.original_ttl)
+        rrlist = sorted(rdataset)
+        for rr in rrlist:
+            hash.update(rrnamebuf)
+            hash.update(rrfixed)
+            rrdata = rr.to_digestable(origin)
+            rrlen = struct.pack('!H', len(rrdata))
+            hash.update(rrlen)
+            hash.update(rrdata)
+
+        try:
+            if _is_rsa(rrsig.algorithm):
+                verifier = pkcs1_15.new(pubkey)
+                # will raise ValueError if verify fails:
+                verifier.verify(hash, sig)
+            elif _is_dsa(rrsig.algorithm):
+                verifier = DSS.new(pubkey, 'fips-186-3')
+                verifier.verify(hash, sig)
+            elif _is_ecdsa(rrsig.algorithm):
+                digest = hash.digest()
+                if not pubkey.verify(digest, sig):
+                    raise ValueError
+            else:
+                # Raise here for code clarity; this won't actually ever happen
+                # since if the algorithm is really unknown we'd already have
+                # raised an exception above
+                raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
+            # If we got here, we successfully verified so we can return without error
+            return
+        except ValueError:
+            # this happens on an individual validation failure
+            continue
+    # nothing verified -- raise failure:
+    raise ValidationFailure('verify failure')
+
+
+def _validate(rrset, rrsigset, keys, origin=None, now=None):
+    """Validate an RRset.
+
+    *rrset* is the RRset to validate.  It can be a ``dns.rrset.RRset`` or
+    a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
+
+    *rrsigset* is the signature RRset to be validated.  It can be a
+    ``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
+
+    *keys* is the key dictionary, used to find the DNSKEY associated with
+    a given name.  The dictionary is keyed by a ``dns.name.Name``, and has
+    ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
+
+    *origin* is a ``dns.name.Name``, the origin to use for relative names.
+
+    *now* is an ``int``, the time to use when validating the signatures,
+    in seconds since the UNIX epoch.  The default is the current time.
+    """
+
+    if isinstance(origin, string_types):
+        origin = dns.name.from_text(origin, dns.name.root)
+
+    if isinstance(rrset, tuple):
+        rrname = rrset[0]
+    else:
+        rrname = rrset.name
+
+    if isinstance(rrsigset, tuple):
+        rrsigname = rrsigset[0]
+        rrsigrdataset = rrsigset[1]
+    else:
+        rrsigname = rrsigset.name
+        rrsigrdataset = rrsigset
+
+    rrname = rrname.choose_relativity(origin)
+    rrsigname = rrsigname.choose_relativity(origin)
+    if rrname != rrsigname:
+        raise ValidationFailure("owner names do not match")
+
+    for rrsig in rrsigrdataset:
+        try:
+            _validate_rrsig(rrset, rrsig, keys, origin, now)
+            return
+        except ValidationFailure:
+            pass
+    raise ValidationFailure("no RRSIGs validated")
+
+
+def _need_pycrypto(*args, **kwargs):
+    raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex")
+
+
+try:
+    try:
+        # test we're using pycryptodome, not pycrypto (which misses SHA1 for example)
+        from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512
+        from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA
+        from Crypto.Signature import pkcs1_15, DSS
+        from Crypto.Util import number
+    except ImportError:
+        from Cryptodome.Hash import MD5, SHA1, SHA256, SHA384, SHA512
+        from Cryptodome.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA
+        from Cryptodome.Signature import pkcs1_15, DSS
+        from Cryptodome.Util import number
+except ImportError:
+    validate = _need_pycrypto
+    validate_rrsig = _need_pycrypto
+    _have_pycrypto = False
+    _have_ecdsa = False
+else:
+    validate = _validate
+    validate_rrsig = _validate_rrsig
+    _have_pycrypto = True
+
+    try:
+        import ecdsa
+        import ecdsa.ecdsa
+        import ecdsa.ellipticcurve
+        import ecdsa.keys
+    except ImportError:
+        _have_ecdsa = False
+    else:
+        _have_ecdsa = True
+
+        class ECKeyWrapper(object):
+
+            def __init__(self, key, key_len):
+                self.key = key
+                self.key_len = key_len
+
+            def verify(self, digest, sig):
+                diglong = number.bytes_to_long(digest)
+                return self.key.pubkey.verifies(diglong, sig)

+ 19 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/dnssec.pyi

@@ -0,0 +1,19 @@
+from typing import Union, Dict, Tuple, Optional
+from . import rdataset, rrset, exception, name, rdtypes, rdata, node
+import dns.rdtypes.ANY.DS as DS
+import dns.rdtypes.ANY.DNSKEY as DNSKEY
+
+_have_ecdsa : bool
+_have_pycrypto : bool
+
+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:
+    ...
+
+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:
+    ...
+
+class ValidationFailure(exception.DNSException):
+    ...
+
+def make_ds(name : name.Name, key : DNSKEY.DNSKEY, algorithm : str, origin : Optional[name.Name] = None) -> DS.DS:
+    ...

+ 105 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/e164.py

@@ -0,0 +1,105 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS E.164 helpers."""
+
+import dns.exception
+import dns.name
+import dns.resolver
+from ._compat import string_types, maybe_decode
+
+#: The public E.164 domain.
+public_enum_domain = dns.name.from_text('e164.arpa.')
+
+
+def from_e164(text, origin=public_enum_domain):
+    """Convert an E.164 number in textual form into a Name object whose
+    value is the ENUM domain name for that number.
+
+    Non-digits in the text are ignored, i.e. "16505551212",
+    "+1.650.555.1212" and "1 (650) 555-1212" are all the same.
+
+    *text*, a ``text``, is an E.164 number in textual form.
+
+    *origin*, a ``dns.name.Name``, the domain in which the number
+    should be constructed.  The default is ``e164.arpa.``.
+
+    Returns a ``dns.name.Name``.
+    """
+
+    parts = [d for d in text if d.isdigit()]
+    parts.reverse()
+    return dns.name.from_text('.'.join(parts), origin=origin)
+
+
+def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
+    """Convert an ENUM domain name into an E.164 number.
+
+    Note that dnspython does not have any information about preferred
+    number formats within national numbering plans, so all numbers are
+    emitted as a simple string of digits, prefixed by a '+' (unless
+    *want_plus_prefix* is ``False``).
+
+    *name* is a ``dns.name.Name``, the ENUM domain name.
+
+    *origin* is a ``dns.name.Name``, a domain containing the ENUM
+    domain name.  The name is relativized to this domain before being
+    converted to text.  If ``None``, no relativization is done.
+
+    *want_plus_prefix* is a ``bool``.  If True, add a '+' to the beginning of
+    the returned number.
+
+    Returns a ``text``.
+
+    """
+    if origin is not None:
+        name = name.relativize(origin)
+    dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1]
+    if len(dlabels) != len(name.labels):
+        raise dns.exception.SyntaxError('non-digit labels in ENUM domain name')
+    dlabels.reverse()
+    text = b''.join(dlabels)
+    if want_plus_prefix:
+        text = b'+' + text
+    return maybe_decode(text)
+
+
+def query(number, domains, resolver=None):
+    """Look for NAPTR RRs for the specified number in the specified domains.
+
+    e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.'])
+
+    *number*, a ``text`` is the number to look for.
+
+    *domains* is an iterable containing ``dns.name.Name`` values.
+
+    *resolver*, a ``dns.resolver.Resolver``, is the resolver to use.  If
+    ``None``, the default resolver is used.
+    """
+
+    if resolver is None:
+        resolver = dns.resolver.get_default_resolver()
+    e_nx = dns.resolver.NXDOMAIN()
+    for domain in domains:
+        if isinstance(domain, string_types):
+            domain = dns.name.from_text(domain)
+        qname = dns.e164.from_e164(number, domain)
+        try:
+            return resolver.query(qname, 'NAPTR')
+        except dns.resolver.NXDOMAIN as e:
+            e_nx += e
+    raise e_nx

+ 10 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/e164.pyi

@@ -0,0 +1,10 @@
+from typing import Optional, Iterable
+from . import name, resolver
+def from_e164(text : str, origin=name.Name(".")) -> name.Name:
+    ...
+
+def to_e164(name : name.Name, origin : Optional[name.Name] = None, want_plus_prefix=True) -> str:
+    ...
+
+def query(number : str, domains : Iterable[str], resolver : Optional[resolver.Resolver] = None) -> resolver.Answer:
+    ...

+ 269 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/edns.py

@@ -0,0 +1,269 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2009-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""EDNS Options"""
+
+from __future__ import absolute_import
+
+import math
+import struct
+
+import dns.inet
+
+#: NSID
+NSID = 3
+#: DAU
+DAU = 5
+#: DHU
+DHU = 6
+#: N3U
+N3U = 7
+#: ECS (client-subnet)
+ECS = 8
+#: EXPIRE
+EXPIRE = 9
+#: COOKIE
+COOKIE = 10
+#: KEEPALIVE
+KEEPALIVE = 11
+#: PADDING
+PADDING = 12
+#: CHAIN
+CHAIN = 13
+
+class Option(object):
+
+    """Base class for all EDNS option types."""
+
+    def __init__(self, otype):
+        """Initialize an option.
+
+        *otype*, an ``int``, is the option type.
+        """
+        self.otype = otype
+
+    def to_wire(self, file):
+        """Convert an option to wire format.
+        """
+        raise NotImplementedError
+
+    @classmethod
+    def from_wire(cls, otype, wire, current, olen):
+        """Build an EDNS option object from wire format.
+
+        *otype*, an ``int``, is the option type.
+
+        *wire*, a ``binary``, is the wire-format message.
+
+        *current*, an ``int``, is the offset in *wire* of the beginning
+        of the rdata.
+
+        *olen*, an ``int``, is the length of the wire-format option data
+
+        Returns a ``dns.edns.Option``.
+        """
+
+        raise NotImplementedError
+
+    def _cmp(self, other):
+        """Compare an EDNS option with another option of the same type.
+
+        Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
+        """
+        raise NotImplementedError
+
+    def __eq__(self, other):
+        if not isinstance(other, Option):
+            return False
+        if self.otype != other.otype:
+            return False
+        return self._cmp(other) == 0
+
+    def __ne__(self, other):
+        if not isinstance(other, Option):
+            return False
+        if self.otype != other.otype:
+            return False
+        return self._cmp(other) != 0
+
+    def __lt__(self, other):
+        if not isinstance(other, Option) or \
+                self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) < 0
+
+    def __le__(self, other):
+        if not isinstance(other, Option) or \
+                self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) <= 0
+
+    def __ge__(self, other):
+        if not isinstance(other, Option) or \
+                self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) >= 0
+
+    def __gt__(self, other):
+        if not isinstance(other, Option) or \
+                self.otype != other.otype:
+            return NotImplemented
+        return self._cmp(other) > 0
+
+
+class GenericOption(Option):
+
+    """Generic Option Class
+
+    This class is used for EDNS option types for which we have no better
+    implementation.
+    """
+
+    def __init__(self, otype, data):
+        super(GenericOption, self).__init__(otype)
+        self.data = data
+
+    def to_wire(self, file):
+        file.write(self.data)
+
+    def to_text(self):
+        return "Generic %d" % self.otype
+
+    @classmethod
+    def from_wire(cls, otype, wire, current, olen):
+        return cls(otype, wire[current: current + olen])
+
+    def _cmp(self, other):
+        if self.data == other.data:
+            return 0
+        if self.data > other.data:
+            return 1
+        return -1
+
+
+class ECSOption(Option):
+    """EDNS Client Subnet (ECS, RFC7871)"""
+
+    def __init__(self, address, srclen=None, scopelen=0):
+        """*address*, a ``text``, is the client address information.
+
+        *srclen*, an ``int``, the source prefix length, which is the
+        leftmost number of bits of the address to be used for the
+        lookup.  The default is 24 for IPv4 and 56 for IPv6.
+
+        *scopelen*, an ``int``, the scope prefix length.  This value
+        must be 0 in queries, and should be set in responses.
+        """
+
+        super(ECSOption, self).__init__(ECS)
+        af = dns.inet.af_for_address(address)
+
+        if af == dns.inet.AF_INET6:
+            self.family = 2
+            if srclen is None:
+                srclen = 56
+        elif af == dns.inet.AF_INET:
+            self.family = 1
+            if srclen is None:
+                srclen = 24
+        else:
+            raise ValueError('Bad ip family')
+
+        self.address = address
+        self.srclen = srclen
+        self.scopelen = scopelen
+
+        addrdata = dns.inet.inet_pton(af, address)
+        nbytes = int(math.ceil(srclen/8.0))
+
+        # Truncate to srclen and pad to the end of the last octet needed
+        # See RFC section 6
+        self.addrdata = addrdata[:nbytes]
+        nbits = srclen % 8
+        if nbits != 0:
+            last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits))
+            self.addrdata = self.addrdata[:-1] + last
+
+    def to_text(self):
+        return "ECS {}/{} scope/{}".format(self.address, self.srclen,
+                                           self.scopelen)
+
+    def to_wire(self, file):
+        file.write(struct.pack('!H', self.family))
+        file.write(struct.pack('!BB', self.srclen, self.scopelen))
+        file.write(self.addrdata)
+
+    @classmethod
+    def from_wire(cls, otype, wire, cur, olen):
+        family, src, scope = struct.unpack('!HBB', wire[cur:cur+4])
+        cur += 4
+
+        addrlen = int(math.ceil(src/8.0))
+
+        if family == 1:
+            af = dns.inet.AF_INET
+            pad = 4 - addrlen
+        elif family == 2:
+            af = dns.inet.AF_INET6
+            pad = 16 - addrlen
+        else:
+            raise ValueError('unsupported family')
+
+        addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad)
+        return cls(addr, src, scope)
+
+    def _cmp(self, other):
+        if self.addrdata == other.addrdata:
+            return 0
+        if self.addrdata > other.addrdata:
+            return 1
+        return -1
+
+_type_to_class = {
+        ECS: ECSOption
+}
+
+def get_option_class(otype):
+    """Return the class for the specified option type.
+
+    The GenericOption class is used if a more specific class is not
+    known.
+    """
+
+    cls = _type_to_class.get(otype)
+    if cls is None:
+        cls = GenericOption
+    return cls
+
+
+def option_from_wire(otype, wire, current, olen):
+    """Build an EDNS option object from wire format.
+
+    *otype*, an ``int``, is the option type.
+
+    *wire*, a ``binary``, is the wire-format message.
+
+    *current*, an ``int``, is the offset in *wire* of the beginning
+    of the rdata.
+
+    *olen*, an ``int``, is the length of the wire-format option data
+
+    Returns an instance of a subclass of ``dns.edns.Option``.
+    """
+
+    cls = get_option_class(otype)
+    return cls.from_wire(otype, wire, current, olen)

+ 148 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/entropy.py

@@ -0,0 +1,148 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2009-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import os
+import random
+import time
+from ._compat import long, binary_type
+try:
+    import threading as _threading
+except ImportError:
+    import dummy_threading as _threading
+
+
+class EntropyPool(object):
+
+    # This is an entropy pool for Python implementations that do not
+    # have a working SystemRandom.  I'm not sure there are any, but
+    # leaving this code doesn't hurt anything as the library code
+    # is used if present.
+
+    def __init__(self, seed=None):
+        self.pool_index = 0
+        self.digest = None
+        self.next_byte = 0
+        self.lock = _threading.Lock()
+        try:
+            import hashlib
+            self.hash = hashlib.sha1()
+            self.hash_len = 20
+        except ImportError:
+            try:
+                import sha
+                self.hash = sha.new()
+                self.hash_len = 20
+            except ImportError:
+                import md5  # pylint: disable=import-error
+                self.hash = md5.new()
+                self.hash_len = 16
+        self.pool = bytearray(b'\0' * self.hash_len)
+        if seed is not None:
+            self.stir(bytearray(seed))
+            self.seeded = True
+            self.seed_pid = os.getpid()
+        else:
+            self.seeded = False
+            self.seed_pid = 0
+
+    def stir(self, entropy, already_locked=False):
+        if not already_locked:
+            self.lock.acquire()
+        try:
+            for c in entropy:
+                if self.pool_index == self.hash_len:
+                    self.pool_index = 0
+                b = c & 0xff
+                self.pool[self.pool_index] ^= b
+                self.pool_index += 1
+        finally:
+            if not already_locked:
+                self.lock.release()
+
+    def _maybe_seed(self):
+        if not self.seeded or self.seed_pid != os.getpid():
+            try:
+                seed = os.urandom(16)
+            except Exception:
+                try:
+                    r = open('/dev/urandom', 'rb', 0)
+                    try:
+                        seed = r.read(16)
+                    finally:
+                        r.close()
+                except Exception:
+                    seed = str(time.time())
+            self.seeded = True
+            self.seed_pid = os.getpid()
+            self.digest = None
+            seed = bytearray(seed)
+            self.stir(seed, True)
+
+    def random_8(self):
+        self.lock.acquire()
+        try:
+            self._maybe_seed()
+            if self.digest is None or self.next_byte == self.hash_len:
+                self.hash.update(binary_type(self.pool))
+                self.digest = bytearray(self.hash.digest())
+                self.stir(self.digest, True)
+                self.next_byte = 0
+            value = self.digest[self.next_byte]
+            self.next_byte += 1
+        finally:
+            self.lock.release()
+        return value
+
+    def random_16(self):
+        return self.random_8() * 256 + self.random_8()
+
+    def random_32(self):
+        return self.random_16() * 65536 + self.random_16()
+
+    def random_between(self, first, last):
+        size = last - first + 1
+        if size > long(4294967296):
+            raise ValueError('too big')
+        if size > 65536:
+            rand = self.random_32
+            max = long(4294967295)
+        elif size > 256:
+            rand = self.random_16
+            max = 65535
+        else:
+            rand = self.random_8
+            max = 255
+        return first + size * rand() // (max + 1)
+
+pool = EntropyPool()
+
+try:
+    system_random = random.SystemRandom()
+except Exception:
+    system_random = None
+
+def random_16():
+    if system_random is not None:
+        return system_random.randrange(0, 65536)
+    else:
+        return pool.random_16()
+
+def between(first, last):
+    if system_random is not None:
+        return system_random.randrange(first, last + 1)
+    else:
+        return pool.random_between(first, last)

+ 10 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/entropy.pyi

@@ -0,0 +1,10 @@
+from typing import Optional
+from random import SystemRandom
+
+system_random : Optional[SystemRandom]
+
+def random_16() -> int:
+   pass
+
+def between(first: int, last: int) -> int:
+    pass

+ 128 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/exception.py

@@ -0,0 +1,128 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Common DNS Exceptions.
+
+Dnspython modules may also define their own exceptions, which will
+always be subclasses of ``DNSException``.
+"""
+
+class DNSException(Exception):
+    """Abstract base class shared by all dnspython exceptions.
+
+    It supports two basic modes of operation:
+
+    a) Old/compatible mode is used if ``__init__`` was called with
+    empty *kwargs*.  In compatible mode all *args* are passed
+    to the standard Python Exception class as before and all *args* are
+    printed by the standard ``__str__`` implementation.  Class variable
+    ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()``
+    if *args* is empty.
+
+    b) New/parametrized mode is used if ``__init__`` was called with
+    non-empty *kwargs*.
+    In the new mode *args* must be empty and all kwargs must match
+    those set in class variable ``supp_kwargs``. All kwargs are stored inside
+    ``self.kwargs`` and used in a new ``__str__`` implementation to construct
+    a formatted message based on the ``fmt`` class variable, a ``string``.
+
+    In the simplest case it is enough to override the ``supp_kwargs``
+    and ``fmt`` class variables to get nice parametrized messages.
+    """
+
+    msg = None  # non-parametrized message
+    supp_kwargs = set()  # accepted parameters for _fmt_kwargs (sanity check)
+    fmt = None  # message parametrized with results from _fmt_kwargs
+
+    def __init__(self, *args, **kwargs):
+        self._check_params(*args, **kwargs)
+        if kwargs:
+            self.kwargs = self._check_kwargs(**kwargs)
+            self.msg = str(self)
+        else:
+            self.kwargs = dict()  # defined but empty for old mode exceptions
+        if self.msg is None:
+            # doc string is better implicit message than empty string
+            self.msg = self.__doc__
+        if args:
+            super(DNSException, self).__init__(*args)
+        else:
+            super(DNSException, self).__init__(self.msg)
+
+    def _check_params(self, *args, **kwargs):
+        """Old exceptions supported only args and not kwargs.
+
+        For sanity we do not allow to mix old and new behavior."""
+        if args or kwargs:
+            assert bool(args) != bool(kwargs), \
+                'keyword arguments are mutually exclusive with positional args'
+
+    def _check_kwargs(self, **kwargs):
+        if kwargs:
+            assert set(kwargs.keys()) == self.supp_kwargs, \
+                'following set of keyword args is required: %s' % (
+                    self.supp_kwargs)
+        return kwargs
+
+    def _fmt_kwargs(self, **kwargs):
+        """Format kwargs before printing them.
+
+        Resulting dictionary has to have keys necessary for str.format call
+        on fmt class variable.
+        """
+        fmtargs = {}
+        for kw, data in kwargs.items():
+            if isinstance(data, (list, set)):
+                # convert list of <someobj> to list of str(<someobj>)
+                fmtargs[kw] = list(map(str, data))
+                if len(fmtargs[kw]) == 1:
+                    # remove list brackets [] from single-item lists
+                    fmtargs[kw] = fmtargs[kw].pop()
+            else:
+                fmtargs[kw] = data
+        return fmtargs
+
+    def __str__(self):
+        if self.kwargs and self.fmt:
+            # provide custom message constructed from keyword arguments
+            fmtargs = self._fmt_kwargs(**self.kwargs)
+            return self.fmt.format(**fmtargs)
+        else:
+            # print *args directly in the same way as old DNSException
+            return super(DNSException, self).__str__()
+
+
+class FormError(DNSException):
+    """DNS message is malformed."""
+
+
+class SyntaxError(DNSException):
+    """Text input is malformed."""
+
+
+class UnexpectedEnd(SyntaxError):
+    """Text input ended unexpectedly."""
+
+
+class TooBig(DNSException):
+    """The DNS message is too big."""
+
+
+class Timeout(DNSException):
+    """The DNS operation timed out."""
+    supp_kwargs = {'timeout'}
+    fmt = "The DNS operation timed out after {timeout} seconds"

+ 9 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/exception.pyi

@@ -0,0 +1,9 @@
+from typing import Set, Optional, Dict
+
+class DNSException(Exception):
+    supp_kwargs : Set[str]
+    kwargs : Optional[Dict]
+
+class SyntaxError(DNSException): ...
+class FormError(DNSException): ...
+class Timeout(DNSException): ...

+ 130 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/flags.py

@@ -0,0 +1,130 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Message Flags."""
+
+# Standard DNS flags
+
+#: Query Response
+QR = 0x8000
+#: Authoritative Answer
+AA = 0x0400
+#: Truncated Response
+TC = 0x0200
+#: Recursion Desired
+RD = 0x0100
+#: Recursion Available
+RA = 0x0080
+#: Authentic Data
+AD = 0x0020
+#: Checking Disabled
+CD = 0x0010
+
+# EDNS flags
+
+#: DNSSEC answer OK
+DO = 0x8000
+
+_by_text = {
+    'QR': QR,
+    'AA': AA,
+    'TC': TC,
+    'RD': RD,
+    'RA': RA,
+    'AD': AD,
+    'CD': CD
+}
+
+_edns_by_text = {
+    'DO': DO
+}
+
+
+# We construct the inverse mappings programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mappings not to be true inverses.
+
+_by_value = {y: x for x, y in _by_text.items()}
+
+_edns_by_value = {y: x for x, y in _edns_by_text.items()}
+
+
+def _order_flags(table):
+    order = list(table.items())
+    order.sort()
+    order.reverse()
+    return order
+
+_flags_order = _order_flags(_by_value)
+
+_edns_flags_order = _order_flags(_edns_by_value)
+
+
+def _from_text(text, table):
+    flags = 0
+    tokens = text.split()
+    for t in tokens:
+        flags = flags | table[t.upper()]
+    return flags
+
+
+def _to_text(flags, table, order):
+    text_flags = []
+    for k, v in order:
+        if flags & k != 0:
+            text_flags.append(v)
+    return ' '.join(text_flags)
+
+
+def from_text(text):
+    """Convert a space-separated list of flag text values into a flags
+    value.
+
+    Returns an ``int``
+    """
+
+    return _from_text(text, _by_text)
+
+
+def to_text(flags):
+    """Convert a flags value into a space-separated list of flag text
+    values.
+
+    Returns a ``text``.
+    """
+
+    return _to_text(flags, _by_value, _flags_order)
+
+
+def edns_from_text(text):
+    """Convert a space-separated list of EDNS flag text values into a EDNS
+    flags value.
+
+    Returns an ``int``
+    """
+
+    return _from_text(text, _edns_by_text)
+
+
+def edns_to_text(flags):
+    """Convert an EDNS flags value into a space-separated list of EDNS flag
+    text values.
+
+    Returns a ``text``.
+    """
+
+    return _to_text(flags, _edns_by_value, _edns_flags_order)

+ 69 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/grange.py

@@ -0,0 +1,69 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2012-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS GENERATE range conversion."""
+
+import dns
+
+def from_text(text):
+    """Convert the text form of a range in a ``$GENERATE`` statement to an
+    integer.
+
+    *text*, a ``str``, the textual range in ``$GENERATE`` form.
+
+    Returns a tuple of three ``int`` values ``(start, stop, step)``.
+    """
+
+    # TODO, figure out the bounds on start, stop and step.
+    step = 1
+    cur = ''
+    state = 0
+    # state   0 1 2 3 4
+    #         x - y / z
+
+    if text and text[0] == '-':
+        raise dns.exception.SyntaxError("Start cannot be a negative number")
+
+    for c in text:
+        if c == '-' and state == 0:
+            start = int(cur)
+            cur = ''
+            state = 2
+        elif c == '/':
+            stop = int(cur)
+            cur = ''
+            state = 4
+        elif c.isdigit():
+            cur += c
+        else:
+            raise dns.exception.SyntaxError("Could not parse %s" % (c))
+
+    if state in (1, 3):
+        raise dns.exception.SyntaxError()
+
+    if state == 2:
+        stop = int(cur)
+
+    if state == 4:
+        step = int(cur)
+
+    assert step >= 1
+    assert start >= 0
+    assert start <= stop
+    # TODO, can start == stop?
+
+    return (start, stop, step)

+ 37 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/hash.py

@@ -0,0 +1,37 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Hashing backwards compatibility wrapper"""
+
+import hashlib
+import warnings
+
+warnings.warn(
+    "dns.hash module will be removed in future versions. Please use hashlib instead.",
+    DeprecationWarning)
+
+hashes = {}
+hashes['MD5'] = hashlib.md5
+hashes['SHA1'] = hashlib.sha1
+hashes['SHA224'] = hashlib.sha224
+hashes['SHA256'] = hashlib.sha256
+hashes['SHA384'] = hashlib.sha384
+hashes['SHA512'] = hashlib.sha512
+
+
+def get(algorithm):
+    return hashes[algorithm.upper()]

+ 124 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/inet.py

@@ -0,0 +1,124 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Generic Internet address helper functions."""
+
+import socket
+
+import dns.ipv4
+import dns.ipv6
+
+from ._compat import maybe_ord
+
+# We assume that AF_INET is always defined.
+
+AF_INET = socket.AF_INET
+
+# AF_INET6 might not be defined in the socket module, but we need it.
+# We'll try to use the socket module's value, and if it doesn't work,
+# we'll use our own value.
+
+try:
+    AF_INET6 = socket.AF_INET6
+except AttributeError:
+    AF_INET6 = 9999
+
+
+def inet_pton(family, text):
+    """Convert the textual form of a network address into its binary form.
+
+    *family* is an ``int``, the address family.
+
+    *text* is a ``text``, the textual address.
+
+    Raises ``NotImplementedError`` if the address family specified is not
+    implemented.
+
+    Returns a ``binary``.
+    """
+
+    if family == AF_INET:
+        return dns.ipv4.inet_aton(text)
+    elif family == AF_INET6:
+        return dns.ipv6.inet_aton(text)
+    else:
+        raise NotImplementedError
+
+
+def inet_ntop(family, address):
+    """Convert the binary form of a network address into its textual form.
+
+    *family* is an ``int``, the address family.
+
+    *address* is a ``binary``, the network address in binary form.
+
+    Raises ``NotImplementedError`` if the address family specified is not
+    implemented.
+
+    Returns a ``text``.
+    """
+
+    if family == AF_INET:
+        return dns.ipv4.inet_ntoa(address)
+    elif family == AF_INET6:
+        return dns.ipv6.inet_ntoa(address)
+    else:
+        raise NotImplementedError
+
+
+def af_for_address(text):
+    """Determine the address family of a textual-form network address.
+
+    *text*, a ``text``, the textual address.
+
+    Raises ``ValueError`` if the address family cannot be determined
+    from the input.
+
+    Returns an ``int``.
+    """
+
+    try:
+        dns.ipv4.inet_aton(text)
+        return AF_INET
+    except Exception:
+        try:
+            dns.ipv6.inet_aton(text)
+            return AF_INET6
+        except:
+            raise ValueError
+
+
+def is_multicast(text):
+    """Is the textual-form network address a multicast address?
+
+    *text*, a ``text``, the textual address.
+
+    Raises ``ValueError`` if the address family cannot be determined
+    from the input.
+
+    Returns a ``bool``.
+    """
+
+    try:
+        first = maybe_ord(dns.ipv4.inet_aton(text)[0])
+        return first >= 224 and first <= 239
+    except Exception:
+        try:
+            first = maybe_ord(dns.ipv6.inet_aton(text)[0])
+            return first == 255
+        except Exception:
+            raise ValueError

+ 4 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/inet.pyi

@@ -0,0 +1,4 @@
+from typing import Union
+from socket import AddressFamily
+
+AF_INET6 : Union[int, AddressFamily]

+ 63 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/ipv4.py

@@ -0,0 +1,63 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""IPv4 helper functions."""
+
+import struct
+
+import dns.exception
+from ._compat import binary_type
+
+def inet_ntoa(address):
+    """Convert an IPv4 address in binary form to text form.
+
+    *address*, a ``binary``, the IPv4 address in binary form.
+
+    Returns a ``text``.
+    """
+
+    if len(address) != 4:
+        raise dns.exception.SyntaxError
+    if not isinstance(address, bytearray):
+        address = bytearray(address)
+    return ('%u.%u.%u.%u' % (address[0], address[1],
+                             address[2], address[3]))
+
+def inet_aton(text):
+    """Convert an IPv4 address in text form to binary form.
+
+    *text*, a ``text``, the IPv4 address in textual form.
+
+    Returns a ``binary``.
+    """
+
+    if not isinstance(text, binary_type):
+        text = text.encode()
+    parts = text.split(b'.')
+    if len(parts) != 4:
+        raise dns.exception.SyntaxError
+    for part in parts:
+        if not part.isdigit():
+            raise dns.exception.SyntaxError
+        if len(part) > 1 and part[0] == '0':
+            # No leading zeros
+            raise dns.exception.SyntaxError
+    try:
+        bytes = [int(part) for part in parts]
+        return struct.pack('BBBB', *bytes)
+    except:
+        raise dns.exception.SyntaxError

+ 181 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/ipv6.py

@@ -0,0 +1,181 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""IPv6 helper functions."""
+
+import re
+import binascii
+
+import dns.exception
+import dns.ipv4
+from ._compat import xrange, binary_type, maybe_decode
+
+_leading_zero = re.compile(r'0+([0-9a-f]+)')
+
+def inet_ntoa(address):
+    """Convert an IPv6 address in binary form to text form.
+
+    *address*, a ``binary``, the IPv6 address in binary form.
+
+    Raises ``ValueError`` if the address isn't 16 bytes long.
+    Returns a ``text``.
+    """
+
+    if len(address) != 16:
+        raise ValueError("IPv6 addresses are 16 bytes long")
+    hex = binascii.hexlify(address)
+    chunks = []
+    i = 0
+    l = len(hex)
+    while i < l:
+        chunk = maybe_decode(hex[i : i + 4])
+        # strip leading zeros.  we do this with an re instead of
+        # with lstrip() because lstrip() didn't support chars until
+        # python 2.2.2
+        m = _leading_zero.match(chunk)
+        if not m is None:
+            chunk = m.group(1)
+        chunks.append(chunk)
+        i += 4
+    #
+    # Compress the longest subsequence of 0-value chunks to ::
+    #
+    best_start = 0
+    best_len = 0
+    start = -1
+    last_was_zero = False
+    for i in xrange(8):
+        if chunks[i] != '0':
+            if last_was_zero:
+                end = i
+                current_len = end - start
+                if current_len > best_len:
+                    best_start = start
+                    best_len = current_len
+                last_was_zero = False
+        elif not last_was_zero:
+            start = i
+            last_was_zero = True
+    if last_was_zero:
+        end = 8
+        current_len = end - start
+        if current_len > best_len:
+            best_start = start
+            best_len = current_len
+    if best_len > 1:
+        if best_start == 0 and \
+           (best_len == 6 or
+            best_len == 5 and chunks[5] == 'ffff'):
+            # We have an embedded IPv4 address
+            if best_len == 6:
+                prefix = '::'
+            else:
+                prefix = '::ffff:'
+            hex = prefix + dns.ipv4.inet_ntoa(address[12:])
+        else:
+            hex = ':'.join(chunks[:best_start]) + '::' + \
+                  ':'.join(chunks[best_start + best_len:])
+    else:
+        hex = ':'.join(chunks)
+    return hex
+
+_v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$')
+_colon_colon_start = re.compile(br'::.*')
+_colon_colon_end = re.compile(br'.*::$')
+
+def inet_aton(text):
+    """Convert an IPv6 address in text form to binary form.
+
+    *text*, a ``text``, the IPv6 address in textual form.
+
+    Returns a ``binary``.
+    """
+
+    #
+    # Our aim here is not something fast; we just want something that works.
+    #
+    if not isinstance(text, binary_type):
+        text = text.encode()
+
+    if text == b'::':
+        text = b'0::'
+    #
+    # Get rid of the icky dot-quad syntax if we have it.
+    #
+    m = _v4_ending.match(text)
+    if not m is None:
+        b = bytearray(dns.ipv4.inet_aton(m.group(2)))
+        text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(),
+                                                       b[0], b[1], b[2],
+                                                       b[3])).encode()
+    #
+    # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
+    # turn '<whatever>::' into '<whatever>:'
+    #
+    m = _colon_colon_start.match(text)
+    if not m is None:
+        text = text[1:]
+    else:
+        m = _colon_colon_end.match(text)
+        if not m is None:
+            text = text[:-1]
+    #
+    # Now canonicalize into 8 chunks of 4 hex digits each
+    #
+    chunks = text.split(b':')
+    l = len(chunks)
+    if l > 8:
+        raise dns.exception.SyntaxError
+    seen_empty = False
+    canonical = []
+    for c in chunks:
+        if c == b'':
+            if seen_empty:
+                raise dns.exception.SyntaxError
+            seen_empty = True
+            for i in xrange(0, 8 - l + 1):
+                canonical.append(b'0000')
+        else:
+            lc = len(c)
+            if lc > 4:
+                raise dns.exception.SyntaxError
+            if lc != 4:
+                c = (b'0' * (4 - lc)) + c
+            canonical.append(c)
+    if l < 8 and not seen_empty:
+        raise dns.exception.SyntaxError
+    text = b''.join(canonical)
+
+    #
+    # Finally we can go to binary.
+    #
+    try:
+        return binascii.unhexlify(text)
+    except (binascii.Error, TypeError):
+        raise dns.exception.SyntaxError
+
+_mapped_prefix = b'\x00' * 10 + b'\xff\xff'
+
+def is_mapped(address):
+    """Is the specified address a mapped IPv4 address?
+
+    *address*, a ``binary`` is an IPv6 address in binary form.
+
+    Returns a ``bool``.
+    """
+
+    return address.startswith(_mapped_prefix)

+ 1175 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/message.py

@@ -0,0 +1,1175 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Messages"""
+
+from __future__ import absolute_import
+
+from io import StringIO
+import struct
+import time
+
+import dns.edns
+import dns.exception
+import dns.flags
+import dns.name
+import dns.opcode
+import dns.entropy
+import dns.rcode
+import dns.rdata
+import dns.rdataclass
+import dns.rdatatype
+import dns.rrset
+import dns.renderer
+import dns.tsig
+import dns.wiredata
+
+from ._compat import long, xrange, string_types
+
+
+class ShortHeader(dns.exception.FormError):
+    """The DNS packet passed to from_wire() is too short."""
+
+
+class TrailingJunk(dns.exception.FormError):
+    """The DNS packet passed to from_wire() has extra junk at the end of it."""
+
+
+class UnknownHeaderField(dns.exception.DNSException):
+    """The header field name was not recognized when converting from text
+    into a message."""
+
+
+class BadEDNS(dns.exception.FormError):
+    """An OPT record occurred somewhere other than the start of
+    the additional data section."""
+
+
+class BadTSIG(dns.exception.FormError):
+    """A TSIG record occurred somewhere other than the end of
+    the additional data section."""
+
+
+class UnknownTSIGKey(dns.exception.DNSException):
+    """A TSIG with an unknown key was received."""
+
+
+#: The question section number
+QUESTION = 0
+
+#: The answer section number
+ANSWER = 1
+
+#: The authority section number
+AUTHORITY = 2
+
+#: The additional section number
+ADDITIONAL = 3
+
+class Message(object):
+    """A DNS message."""
+
+    def __init__(self, id=None):
+        if id is None:
+            self.id = dns.entropy.random_16()
+        else:
+            self.id = id
+        self.flags = 0
+        self.question = []
+        self.answer = []
+        self.authority = []
+        self.additional = []
+        self.edns = -1
+        self.ednsflags = 0
+        self.payload = 0
+        self.options = []
+        self.request_payload = 0
+        self.keyring = None
+        self.keyname = None
+        self.keyalgorithm = dns.tsig.default_algorithm
+        self.request_mac = b''
+        self.other_data = b''
+        self.tsig_error = 0
+        self.fudge = 300
+        self.original_id = self.id
+        self.mac = b''
+        self.xfr = False
+        self.origin = None
+        self.tsig_ctx = None
+        self.had_tsig = False
+        self.multi = False
+        self.first = True
+        self.index = {}
+
+    def __repr__(self):
+        return '<DNS message, ID ' + repr(self.id) + '>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        """Convert the message to text.
+
+        The *origin*, *relativize*, and any other keyword
+        arguments are passed to the RRset ``to_wire()`` method.
+
+        Returns a ``text``.
+        """
+
+        s = StringIO()
+        s.write(u'id %d\n' % self.id)
+        s.write(u'opcode %s\n' %
+                dns.opcode.to_text(dns.opcode.from_flags(self.flags)))
+        rc = dns.rcode.from_flags(self.flags, self.ednsflags)
+        s.write(u'rcode %s\n' % dns.rcode.to_text(rc))
+        s.write(u'flags %s\n' % dns.flags.to_text(self.flags))
+        if self.edns >= 0:
+            s.write(u'edns %s\n' % self.edns)
+            if self.ednsflags != 0:
+                s.write(u'eflags %s\n' %
+                        dns.flags.edns_to_text(self.ednsflags))
+            s.write(u'payload %d\n' % self.payload)
+        for opt in self.options:
+            s.write(u'option %s\n' % opt.to_text())
+        is_update = dns.opcode.is_update(self.flags)
+        if is_update:
+            s.write(u';ZONE\n')
+        else:
+            s.write(u';QUESTION\n')
+        for rrset in self.question:
+            s.write(rrset.to_text(origin, relativize, **kw))
+            s.write(u'\n')
+        if is_update:
+            s.write(u';PREREQ\n')
+        else:
+            s.write(u';ANSWER\n')
+        for rrset in self.answer:
+            s.write(rrset.to_text(origin, relativize, **kw))
+            s.write(u'\n')
+        if is_update:
+            s.write(u';UPDATE\n')
+        else:
+            s.write(u';AUTHORITY\n')
+        for rrset in self.authority:
+            s.write(rrset.to_text(origin, relativize, **kw))
+            s.write(u'\n')
+        s.write(u';ADDITIONAL\n')
+        for rrset in self.additional:
+            s.write(rrset.to_text(origin, relativize, **kw))
+            s.write(u'\n')
+        #
+        # We strip off the final \n so the caller can print the result without
+        # doing weird things to get around eccentricities in Python print
+        # formatting
+        #
+        return s.getvalue()[:-1]
+
+    def __eq__(self, other):
+        """Two messages are equal if they have the same content in the
+        header, question, answer, and authority sections.
+
+        Returns a ``bool``.
+        """
+
+        if not isinstance(other, Message):
+            return False
+        if self.id != other.id:
+            return False
+        if self.flags != other.flags:
+            return False
+        for n in self.question:
+            if n not in other.question:
+                return False
+        for n in other.question:
+            if n not in self.question:
+                return False
+        for n in self.answer:
+            if n not in other.answer:
+                return False
+        for n in other.answer:
+            if n not in self.answer:
+                return False
+        for n in self.authority:
+            if n not in other.authority:
+                return False
+        for n in other.authority:
+            if n not in self.authority:
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def is_response(self, other):
+        """Is this message a response to *other*?
+
+        Returns a ``bool``.
+        """
+
+        if other.flags & dns.flags.QR == 0 or \
+           self.id != other.id or \
+           dns.opcode.from_flags(self.flags) != \
+           dns.opcode.from_flags(other.flags):
+            return False
+        if dns.rcode.from_flags(other.flags, other.ednsflags) != \
+                dns.rcode.NOERROR:
+            return True
+        if dns.opcode.is_update(self.flags):
+            return True
+        for n in self.question:
+            if n not in other.question:
+                return False
+        for n in other.question:
+            if n not in self.question:
+                return False
+        return True
+
+    def section_number(self, section):
+        """Return the "section number" of the specified section for use
+        in indexing.  The question section is 0, the answer section is 1,
+        the authority section is 2, and the additional section is 3.
+
+        *section* is one of the section attributes of this message.
+
+        Raises ``ValueError`` if the section isn't known.
+
+        Returns an ``int``.
+        """
+
+        if section is self.question:
+            return QUESTION
+        elif section is self.answer:
+            return ANSWER
+        elif section is self.authority:
+            return AUTHORITY
+        elif section is self.additional:
+            return ADDITIONAL
+        else:
+            raise ValueError('unknown section')
+
+    def section_from_number(self, number):
+        """Return the "section number" of the specified section for use
+        in indexing.  The question section is 0, the answer section is 1,
+        the authority section is 2, and the additional section is 3.
+
+        *section* is one of the section attributes of this message.
+
+        Raises ``ValueError`` if the section isn't known.
+
+        Returns an ``int``.
+        """
+
+        if number == QUESTION:
+            return self.question
+        elif number == ANSWER:
+            return self.answer
+        elif number == AUTHORITY:
+            return self.authority
+        elif number == ADDITIONAL:
+            return self.additional
+        else:
+            raise ValueError('unknown section')
+
+    def find_rrset(self, section, name, rdclass, rdtype,
+                   covers=dns.rdatatype.NONE, deleting=None, create=False,
+                   force_unique=False):
+        """Find the RRset with the given attributes in the specified section.
+
+        *section*, an ``int`` section number, or one of the section
+        attributes of this message.  This specifies the
+        the section of the message to search.  For example::
+
+            my_message.find_rrset(my_message.answer, name, rdclass, rdtype)
+            my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype)
+
+        *name*, a ``dns.name.Name``, the name of the RRset.
+
+        *rdclass*, an ``int``, the class of the RRset.
+
+        *rdtype*, an ``int``, the type of the RRset.
+
+        *covers*, an ``int`` or ``None``, the covers value of the RRset.
+        The default is ``None``.
+
+        *deleting*, an ``int`` or ``None``, the deleting value of the RRset.
+        The default is ``None``.
+
+        *create*, a ``bool``.  If ``True``, create the RRset if it is not found.
+        The created RRset is appended to *section*.
+
+        *force_unique*, a ``bool``.  If ``True`` and *create* is also ``True``,
+        create a new RRset regardless of whether a matching RRset exists
+        already.  The default is ``False``.  This is useful when creating
+        DDNS Update messages, as order matters for them.
+
+        Raises ``KeyError`` if the RRset was not found and create was
+        ``False``.
+
+        Returns a ``dns.rrset.RRset object``.
+        """
+
+        if isinstance(section, int):
+            section_number = section
+            section = self.section_from_number(section_number)
+        else:
+            section_number = self.section_number(section)
+        key = (section_number, name, rdclass, rdtype, covers, deleting)
+        if not force_unique:
+            if self.index is not None:
+                rrset = self.index.get(key)
+                if rrset is not None:
+                    return rrset
+            else:
+                for rrset in section:
+                    if rrset.match(name, rdclass, rdtype, covers, deleting):
+                        return rrset
+        if not create:
+            raise KeyError
+        rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting)
+        section.append(rrset)
+        if self.index is not None:
+            self.index[key] = rrset
+        return rrset
+
+    def get_rrset(self, section, name, rdclass, rdtype,
+                  covers=dns.rdatatype.NONE, deleting=None, create=False,
+                  force_unique=False):
+        """Get the RRset with the given attributes in the specified section.
+
+        If the RRset is not found, None is returned.
+
+        *section*, an ``int`` section number, or one of the section
+        attributes of this message.  This specifies the
+        the section of the message to search.  For example::
+
+            my_message.get_rrset(my_message.answer, name, rdclass, rdtype)
+            my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype)
+
+        *name*, a ``dns.name.Name``, the name of the RRset.
+
+        *rdclass*, an ``int``, the class of the RRset.
+
+        *rdtype*, an ``int``, the type of the RRset.
+
+        *covers*, an ``int`` or ``None``, the covers value of the RRset.
+        The default is ``None``.
+
+        *deleting*, an ``int`` or ``None``, the deleting value of the RRset.
+        The default is ``None``.
+
+        *create*, a ``bool``.  If ``True``, create the RRset if it is not found.
+        The created RRset is appended to *section*.
+
+        *force_unique*, a ``bool``.  If ``True`` and *create* is also ``True``,
+        create a new RRset regardless of whether a matching RRset exists
+        already.  The default is ``False``.  This is useful when creating
+        DDNS Update messages, as order matters for them.
+
+        Returns a ``dns.rrset.RRset object`` or ``None``.
+        """
+
+        try:
+            rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
+                                    deleting, create, force_unique)
+        except KeyError:
+            rrset = None
+        return rrset
+
+    def to_wire(self, origin=None, max_size=0, **kw):
+        """Return a string containing the message in DNS compressed wire
+        format.
+
+        Additional keyword arguments are passed to the RRset ``to_wire()``
+        method.
+
+        *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended
+        to any relative names.
+
+        *max_size*, an ``int``, the maximum size of the wire format
+        output; default is 0, which means "the message's request
+        payload, if nonzero, or 65535".
+
+        Raises ``dns.exception.TooBig`` if *max_size* was exceeded.
+
+        Returns a ``binary``.
+        """
+
+        if max_size == 0:
+            if self.request_payload != 0:
+                max_size = self.request_payload
+            else:
+                max_size = 65535
+        if max_size < 512:
+            max_size = 512
+        elif max_size > 65535:
+            max_size = 65535
+        r = dns.renderer.Renderer(self.id, self.flags, max_size, origin)
+        for rrset in self.question:
+            r.add_question(rrset.name, rrset.rdtype, rrset.rdclass)
+        for rrset in self.answer:
+            r.add_rrset(dns.renderer.ANSWER, rrset, **kw)
+        for rrset in self.authority:
+            r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw)
+        if self.edns >= 0:
+            r.add_edns(self.edns, self.ednsflags, self.payload, self.options)
+        for rrset in self.additional:
+            r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw)
+        r.write_header()
+        if self.keyname is not None:
+            r.add_tsig(self.keyname, self.keyring[self.keyname],
+                       self.fudge, self.original_id, self.tsig_error,
+                       self.other_data, self.request_mac,
+                       self.keyalgorithm)
+            self.mac = r.mac
+        return r.get_wire()
+
+    def use_tsig(self, keyring, keyname=None, fudge=300,
+                 original_id=None, tsig_error=0, other_data=b'',
+                 algorithm=dns.tsig.default_algorithm):
+        """When sending, a TSIG signature using the specified keyring
+        and keyname should be added.
+
+        See the documentation of the Message class for a complete
+        description of the keyring dictionary.
+
+        *keyring*, a ``dict``, the TSIG keyring to use.  If a
+        *keyring* is specified but a *keyname* is not, then the key
+        used will be the first key in the *keyring*.  Note that the
+        order of keys in a dictionary is not defined, so applications
+        should supply a keyname when a keyring is used, unless they
+        know the keyring contains only one key.
+
+        *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key
+        to use; defaults to ``None``. The key must be defined in the keyring.
+
+        *fudge*, an ``int``, the TSIG time fudge.
+
+        *original_id*, an ``int``, the TSIG original id.  If ``None``,
+        the message's id is used.
+
+        *tsig_error*, an ``int``, the TSIG error code.
+
+        *other_data*, a ``binary``, the TSIG other data.
+
+        *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use.
+        """
+
+        self.keyring = keyring
+        if keyname is None:
+            self.keyname = list(self.keyring.keys())[0]
+        else:
+            if isinstance(keyname, string_types):
+                keyname = dns.name.from_text(keyname)
+            self.keyname = keyname
+        self.keyalgorithm = algorithm
+        self.fudge = fudge
+        if original_id is None:
+            self.original_id = self.id
+        else:
+            self.original_id = original_id
+        self.tsig_error = tsig_error
+        self.other_data = other_data
+
+    def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None,
+                 options=None):
+        """Configure EDNS behavior.
+
+        *edns*, an ``int``, is the EDNS level to use.  Specifying
+        ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case
+        the other parameters are ignored.  Specifying ``True`` is
+        equivalent to specifying 0, i.e. "use EDNS0".
+
+        *ednsflags*, an ``int``, the EDNS flag values.
+
+        *payload*, an ``int``, is the EDNS sender's payload field, which is the
+        maximum size of UDP datagram the sender can handle.  I.e. how big
+        a response to this message can be.
+
+        *request_payload*, an ``int``, is the EDNS payload size to use when
+        sending this message.  If not specified, defaults to the value of
+        *payload*.
+
+        *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS
+        options.
+        """
+
+        if edns is None or edns is False:
+            edns = -1
+        if edns is True:
+            edns = 0
+        if request_payload is None:
+            request_payload = payload
+        if edns < 0:
+            ednsflags = 0
+            payload = 0
+            request_payload = 0
+            options = []
+        else:
+            # make sure the EDNS version in ednsflags agrees with edns
+            ednsflags &= long(0xFF00FFFF)
+            ednsflags |= (edns << 16)
+            if options is None:
+                options = []
+        self.edns = edns
+        self.ednsflags = ednsflags
+        self.payload = payload
+        self.options = options
+        self.request_payload = request_payload
+
+    def want_dnssec(self, wanted=True):
+        """Enable or disable 'DNSSEC desired' flag in requests.
+
+        *wanted*, a ``bool``.  If ``True``, then DNSSEC data is
+        desired in the response, EDNS is enabled if required, and then
+        the DO bit is set.  If ``False``, the DO bit is cleared if
+        EDNS is enabled.
+        """
+
+        if wanted:
+            if self.edns < 0:
+                self.use_edns()
+            self.ednsflags |= dns.flags.DO
+        elif self.edns >= 0:
+            self.ednsflags &= ~dns.flags.DO
+
+    def rcode(self):
+        """Return the rcode.
+
+        Returns an ``int``.
+        """
+        return dns.rcode.from_flags(self.flags, self.ednsflags)
+
+    def set_rcode(self, rcode):
+        """Set the rcode.
+
+        *rcode*, an ``int``, is the rcode to set.
+        """
+        (value, evalue) = dns.rcode.to_flags(rcode)
+        self.flags &= 0xFFF0
+        self.flags |= value
+        self.ednsflags &= long(0x00FFFFFF)
+        self.ednsflags |= evalue
+        if self.ednsflags != 0 and self.edns < 0:
+            self.edns = 0
+
+    def opcode(self):
+        """Return the opcode.
+
+        Returns an ``int``.
+        """
+        return dns.opcode.from_flags(self.flags)
+
+    def set_opcode(self, opcode):
+        """Set the opcode.
+
+        *opcode*, an ``int``, is the opcode to set.
+        """
+        self.flags &= 0x87FF
+        self.flags |= dns.opcode.to_flags(opcode)
+
+
+class _WireReader(object):
+
+    """Wire format reader.
+
+    wire: a binary, is the wire-format message.
+    message: The message object being built
+    current: When building a message object from wire format, this
+    variable contains the offset from the beginning of wire of the next octet
+    to be read.
+    updating: Is the message a dynamic update?
+    one_rr_per_rrset: Put each RR into its own RRset?
+    ignore_trailing: Ignore trailing junk at end of request?
+    zone_rdclass: The class of the zone in messages which are
+    DNS dynamic updates.
+    """
+
+    def __init__(self, wire, message, question_only=False,
+                 one_rr_per_rrset=False, ignore_trailing=False):
+        self.wire = dns.wiredata.maybe_wrap(wire)
+        self.message = message
+        self.current = 0
+        self.updating = False
+        self.zone_rdclass = dns.rdataclass.IN
+        self.question_only = question_only
+        self.one_rr_per_rrset = one_rr_per_rrset
+        self.ignore_trailing = ignore_trailing
+
+    def _get_question(self, qcount):
+        """Read the next *qcount* records from the wire data and add them to
+        the question section.
+        """
+
+        if self.updating and qcount > 1:
+            raise dns.exception.FormError
+
+        for i in xrange(0, qcount):
+            (qname, used) = dns.name.from_wire(self.wire, self.current)
+            if self.message.origin is not None:
+                qname = qname.relativize(self.message.origin)
+            self.current = self.current + used
+            (rdtype, rdclass) = \
+                struct.unpack('!HH',
+                              self.wire[self.current:self.current + 4])
+            self.current = self.current + 4
+            self.message.find_rrset(self.message.question, qname,
+                                    rdclass, rdtype, create=True,
+                                    force_unique=True)
+            if self.updating:
+                self.zone_rdclass = rdclass
+
+    def _get_section(self, section, count):
+        """Read the next I{count} records from the wire data and add them to
+        the specified section.
+
+        section: the section of the message to which to add records
+        count: the number of records to read
+        """
+
+        if self.updating or self.one_rr_per_rrset:
+            force_unique = True
+        else:
+            force_unique = False
+        seen_opt = False
+        for i in xrange(0, count):
+            rr_start = self.current
+            (name, used) = dns.name.from_wire(self.wire, self.current)
+            absolute_name = name
+            if self.message.origin is not None:
+                name = name.relativize(self.message.origin)
+            self.current = self.current + used
+            (rdtype, rdclass, ttl, rdlen) = \
+                struct.unpack('!HHIH',
+                              self.wire[self.current:self.current + 10])
+            self.current = self.current + 10
+            if rdtype == dns.rdatatype.OPT:
+                if section is not self.message.additional or seen_opt:
+                    raise BadEDNS
+                self.message.payload = rdclass
+                self.message.ednsflags = ttl
+                self.message.edns = (ttl & 0xff0000) >> 16
+                self.message.options = []
+                current = self.current
+                optslen = rdlen
+                while optslen > 0:
+                    (otype, olen) = \
+                        struct.unpack('!HH',
+                                      self.wire[current:current + 4])
+                    current = current + 4
+                    opt = dns.edns.option_from_wire(
+                        otype, self.wire, current, olen)
+                    self.message.options.append(opt)
+                    current = current + olen
+                    optslen = optslen - 4 - olen
+                seen_opt = True
+            elif rdtype == dns.rdatatype.TSIG:
+                if not (section is self.message.additional and
+                        i == (count - 1)):
+                    raise BadTSIG
+                if self.message.keyring is None:
+                    raise UnknownTSIGKey('got signed message without keyring')
+                secret = self.message.keyring.get(absolute_name)
+                if secret is None:
+                    raise UnknownTSIGKey("key '%s' unknown" % name)
+                self.message.keyname = absolute_name
+                (self.message.keyalgorithm, self.message.mac) = \
+                    dns.tsig.get_algorithm_and_mac(self.wire, self.current,
+                                                   rdlen)
+                self.message.tsig_ctx = \
+                    dns.tsig.validate(self.wire,
+                                      absolute_name,
+                                      secret,
+                                      int(time.time()),
+                                      self.message.request_mac,
+                                      rr_start,
+                                      self.current,
+                                      rdlen,
+                                      self.message.tsig_ctx,
+                                      self.message.multi,
+                                      self.message.first)
+                self.message.had_tsig = True
+            else:
+                if ttl < 0:
+                    ttl = 0
+                if self.updating and \
+                   (rdclass == dns.rdataclass.ANY or
+                        rdclass == dns.rdataclass.NONE):
+                    deleting = rdclass
+                    rdclass = self.zone_rdclass
+                else:
+                    deleting = None
+                if deleting == dns.rdataclass.ANY or \
+                   (deleting == dns.rdataclass.NONE and
+                        section is self.message.answer):
+                    covers = dns.rdatatype.NONE
+                    rd = None
+                else:
+                    rd = dns.rdata.from_wire(rdclass, rdtype, self.wire,
+                                             self.current, rdlen,
+                                             self.message.origin)
+                    covers = rd.covers()
+                if self.message.xfr and rdtype == dns.rdatatype.SOA:
+                    force_unique = True
+                rrset = self.message.find_rrset(section, name,
+                                                rdclass, rdtype, covers,
+                                                deleting, True, force_unique)
+                if rd is not None:
+                    rrset.add(rd, ttl)
+            self.current = self.current + rdlen
+
+    def read(self):
+        """Read a wire format DNS message and build a dns.message.Message
+        object."""
+
+        l = len(self.wire)
+        if l < 12:
+            raise ShortHeader
+        (self.message.id, self.message.flags, qcount, ancount,
+         aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12])
+        self.current = 12
+        if dns.opcode.is_update(self.message.flags):
+            self.updating = True
+        self._get_question(qcount)
+        if self.question_only:
+            return
+        self._get_section(self.message.answer, ancount)
+        self._get_section(self.message.authority, aucount)
+        self._get_section(self.message.additional, adcount)
+        if not self.ignore_trailing and self.current != l:
+            raise TrailingJunk
+        if self.message.multi and self.message.tsig_ctx and \
+                not self.message.had_tsig:
+            self.message.tsig_ctx.update(self.wire)
+
+
+def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None,
+              tsig_ctx=None, multi=False, first=True,
+              question_only=False, one_rr_per_rrset=False,
+              ignore_trailing=False):
+    """Convert a DNS wire format message into a message
+    object.
+
+    *keyring*, a ``dict``, the keyring to use if the message is signed.
+
+    *request_mac*, a ``binary``.  If the message is a response to a
+    TSIG-signed request, *request_mac* should be set to the MAC of
+    that request.
+
+    *xfr*, a ``bool``, should be set to ``True`` if this message is part of
+    a zone transfer.
+
+    *origin*, a ``dns.name.Name`` or ``None``.  If the message is part
+    of a zone transfer, *origin* should be the origin name of the
+    zone.
+
+    *tsig_ctx*, a ``hmac.HMAC`` objext, the ongoing TSIG context, used
+    when validating zone transfers.
+
+    *multi*, a ``bool``, should be set to ``True`` if this message
+    part of a multiple message sequence.
+
+    *first*, a ``bool``, should be set to ``True`` if this message is
+    stand-alone, or the first message in a multi-message sequence.
+
+    *question_only*, a ``bool``.  If ``True``, read only up to
+    the end of the question section.
+
+    *one_rr_per_rrset*, a ``bool``.  If ``True``, put each RR into its
+    own RRset.
+
+    *ignore_trailing*, a ``bool``.  If ``True``, ignore trailing
+    junk at end of the message.
+
+    Raises ``dns.message.ShortHeader`` if the message is less than 12 octets
+    long.
+
+    Raises ``dns.messaage.TrailingJunk`` if there were octets in the message
+    past the end of the proper DNS message, and *ignore_trailing* is ``False``.
+
+    Raises ``dns.message.BadEDNS`` if an OPT record was in the
+    wrong section, or occurred more than once.
+
+    Raises ``dns.message.BadTSIG`` if a TSIG record was not the last
+    record of the additional data section.
+
+    Returns a ``dns.message.Message``.
+    """
+
+    m = Message(id=0)
+    m.keyring = keyring
+    m.request_mac = request_mac
+    m.xfr = xfr
+    m.origin = origin
+    m.tsig_ctx = tsig_ctx
+    m.multi = multi
+    m.first = first
+
+    reader = _WireReader(wire, m, question_only, one_rr_per_rrset,
+                         ignore_trailing)
+    reader.read()
+
+    return m
+
+
+class _TextReader(object):
+
+    """Text format reader.
+
+    tok: the tokenizer.
+    message: The message object being built.
+    updating: Is the message a dynamic update?
+    zone_rdclass: The class of the zone in messages which are
+    DNS dynamic updates.
+    last_name: The most recently read name when building a message object.
+    """
+
+    def __init__(self, text, message):
+        self.message = message
+        self.tok = dns.tokenizer.Tokenizer(text)
+        self.last_name = None
+        self.zone_rdclass = dns.rdataclass.IN
+        self.updating = False
+
+    def _header_line(self, section):
+        """Process one line from the text format header section."""
+
+        token = self.tok.get()
+        what = token.value
+        if what == 'id':
+            self.message.id = self.tok.get_int()
+        elif what == 'flags':
+            while True:
+                token = self.tok.get()
+                if not token.is_identifier():
+                    self.tok.unget(token)
+                    break
+                self.message.flags = self.message.flags | \
+                    dns.flags.from_text(token.value)
+            if dns.opcode.is_update(self.message.flags):
+                self.updating = True
+        elif what == 'edns':
+            self.message.edns = self.tok.get_int()
+            self.message.ednsflags = self.message.ednsflags | \
+                (self.message.edns << 16)
+        elif what == 'eflags':
+            if self.message.edns < 0:
+                self.message.edns = 0
+            while True:
+                token = self.tok.get()
+                if not token.is_identifier():
+                    self.tok.unget(token)
+                    break
+                self.message.ednsflags = self.message.ednsflags | \
+                    dns.flags.edns_from_text(token.value)
+        elif what == 'payload':
+            self.message.payload = self.tok.get_int()
+            if self.message.edns < 0:
+                self.message.edns = 0
+        elif what == 'opcode':
+            text = self.tok.get_string()
+            self.message.flags = self.message.flags | \
+                dns.opcode.to_flags(dns.opcode.from_text(text))
+        elif what == 'rcode':
+            text = self.tok.get_string()
+            self.message.set_rcode(dns.rcode.from_text(text))
+        else:
+            raise UnknownHeaderField
+        self.tok.get_eol()
+
+    def _question_line(self, section):
+        """Process one line from the text format question section."""
+
+        token = self.tok.get(want_leading=True)
+        if not token.is_whitespace():
+            self.last_name = dns.name.from_text(token.value, None)
+        name = self.last_name
+        token = self.tok.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError
+        # Class
+        try:
+            rdclass = dns.rdataclass.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except Exception:
+            rdclass = dns.rdataclass.IN
+        # Type
+        rdtype = dns.rdatatype.from_text(token.value)
+        self.message.find_rrset(self.message.question, name,
+                                rdclass, rdtype, create=True,
+                                force_unique=True)
+        if self.updating:
+            self.zone_rdclass = rdclass
+        self.tok.get_eol()
+
+    def _rr_line(self, section):
+        """Process one line from the text format answer, authority, or
+        additional data sections.
+        """
+
+        deleting = None
+        # Name
+        token = self.tok.get(want_leading=True)
+        if not token.is_whitespace():
+            self.last_name = dns.name.from_text(token.value, None)
+        name = self.last_name
+        token = self.tok.get()
+        if not token.is_identifier():
+            raise dns.exception.SyntaxError
+        # TTL
+        try:
+            ttl = int(token.value, 0)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except Exception:
+            ttl = 0
+        # Class
+        try:
+            rdclass = dns.rdataclass.from_text(token.value)
+            token = self.tok.get()
+            if not token.is_identifier():
+                raise dns.exception.SyntaxError
+            if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
+                deleting = rdclass
+                rdclass = self.zone_rdclass
+        except dns.exception.SyntaxError:
+            raise dns.exception.SyntaxError
+        except Exception:
+            rdclass = dns.rdataclass.IN
+        # Type
+        rdtype = dns.rdatatype.from_text(token.value)
+        token = self.tok.get()
+        if not token.is_eol_or_eof():
+            self.tok.unget(token)
+            rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
+            covers = rd.covers()
+        else:
+            rd = None
+            covers = dns.rdatatype.NONE
+        rrset = self.message.find_rrset(section, name,
+                                        rdclass, rdtype, covers,
+                                        deleting, True, self.updating)
+        if rd is not None:
+            rrset.add(rd, ttl)
+
+    def read(self):
+        """Read a text format DNS message and build a dns.message.Message
+        object."""
+
+        line_method = self._header_line
+        section = None
+        while 1:
+            token = self.tok.get(True, True)
+            if token.is_eol_or_eof():
+                break
+            if token.is_comment():
+                u = token.value.upper()
+                if u == 'HEADER':
+                    line_method = self._header_line
+                elif u == 'QUESTION' or u == 'ZONE':
+                    line_method = self._question_line
+                    section = self.message.question
+                elif u == 'ANSWER' or u == 'PREREQ':
+                    line_method = self._rr_line
+                    section = self.message.answer
+                elif u == 'AUTHORITY' or u == 'UPDATE':
+                    line_method = self._rr_line
+                    section = self.message.authority
+                elif u == 'ADDITIONAL':
+                    line_method = self._rr_line
+                    section = self.message.additional
+                self.tok.get_eol()
+                continue
+            self.tok.unget(token)
+            line_method(section)
+
+
+def from_text(text):
+    """Convert the text format message into a message object.
+
+    *text*, a ``text``, the text format message.
+
+    Raises ``dns.message.UnknownHeaderField`` if a header is unknown.
+
+    Raises ``dns.exception.SyntaxError`` if the text is badly formed.
+
+    Returns a ``dns.message.Message object``
+    """
+
+    # 'text' can also be a file, but we don't publish that fact
+    # since it's an implementation detail.  The official file
+    # interface is from_file().
+
+    m = Message()
+
+    reader = _TextReader(text, m)
+    reader.read()
+
+    return m
+
+
+def from_file(f):
+    """Read the next text format message from the specified file.
+
+    *f*, a ``file`` or ``text``.  If *f* is text, it is treated as the
+    pathname of a file to open.
+
+    Raises ``dns.message.UnknownHeaderField`` if a header is unknown.
+
+    Raises ``dns.exception.SyntaxError`` if the text is badly formed.
+
+    Returns a ``dns.message.Message object``
+    """
+
+    str_type = string_types
+    opts = 'rU'
+
+    if isinstance(f, str_type):
+        f = open(f, opts)
+        want_close = True
+    else:
+        want_close = False
+
+    try:
+        m = from_text(f)
+    finally:
+        if want_close:
+            f.close()
+    return m
+
+
+def make_query(qname, rdtype, rdclass=dns.rdataclass.IN, use_edns=None,
+               want_dnssec=False, ednsflags=None, payload=None,
+               request_payload=None, options=None):
+    """Make a query message.
+
+    The query name, type, and class may all be specified either
+    as objects of the appropriate type, or as strings.
+
+    The query will have a randomly chosen query id, and its DNS flags
+    will be set to dns.flags.RD.
+
+    qname, a ``dns.name.Name`` or ``text``, the query name.
+
+    *rdtype*, an ``int`` or ``text``, the desired rdata type.
+
+    *rdclass*, an ``int`` or ``text``,  the desired rdata class; the default
+    is class IN.
+
+    *use_edns*, an ``int``, ``bool`` or ``None``.  The EDNS level to use; the
+    default is None (no EDNS).
+    See the description of dns.message.Message.use_edns() for the possible
+    values for use_edns and their meanings.
+
+    *want_dnssec*, a ``bool``.  If ``True``, DNSSEC data is desired.
+
+    *ednsflags*, an ``int``, the EDNS flag values.
+
+    *payload*, an ``int``, is the EDNS sender's payload field, which is the
+    maximum size of UDP datagram the sender can handle.  I.e. how big
+    a response to this message can be.
+
+    *request_payload*, an ``int``, is the EDNS payload size to use when
+    sending this message.  If not specified, defaults to the value of
+    *payload*.
+
+    *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS
+    options.
+
+    Returns a ``dns.message.Message``
+    """
+
+    if isinstance(qname, string_types):
+        qname = dns.name.from_text(qname)
+    if isinstance(rdtype, string_types):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    if isinstance(rdclass, string_types):
+        rdclass = dns.rdataclass.from_text(rdclass)
+    m = Message()
+    m.flags |= dns.flags.RD
+    m.find_rrset(m.question, qname, rdclass, rdtype, create=True,
+                 force_unique=True)
+    # only pass keywords on to use_edns if they have been set to a
+    # non-None value.  Setting a field will turn EDNS on if it hasn't
+    # been configured.
+    kwargs = {}
+    if ednsflags is not None:
+        kwargs['ednsflags'] = ednsflags
+        if use_edns is None:
+            use_edns = 0
+    if payload is not None:
+        kwargs['payload'] = payload
+        if use_edns is None:
+            use_edns = 0
+    if request_payload is not None:
+        kwargs['request_payload'] = request_payload
+        if use_edns is None:
+            use_edns = 0
+    if options is not None:
+        kwargs['options'] = options
+        if use_edns is None:
+            use_edns = 0
+    kwargs['edns'] = use_edns
+    m.use_edns(**kwargs)
+    m.want_dnssec(want_dnssec)
+    return m
+
+
+def make_response(query, recursion_available=False, our_payload=8192,
+                  fudge=300):
+    """Make a message which is a response for the specified query.
+    The message returned is really a response skeleton; it has all
+    of the infrastructure required of a response, but none of the
+    content.
+
+    The response's question section is a shallow copy of the query's
+    question section, so the query's question RRsets should not be
+    changed.
+
+    *query*, a ``dns.message.Message``, the query to respond to.
+
+    *recursion_available*, a ``bool``, should RA be set in the response?
+
+    *our_payload*, an ``int``, the payload size to advertise in EDNS
+    responses.
+
+    *fudge*, an ``int``, the TSIG time fudge.
+
+    Returns a ``dns.message.Message`` object.
+    """
+
+    if query.flags & dns.flags.QR:
+        raise dns.exception.FormError('specified query message is not a query')
+    response = dns.message.Message(query.id)
+    response.flags = dns.flags.QR | (query.flags & dns.flags.RD)
+    if recursion_available:
+        response.flags |= dns.flags.RA
+    response.set_opcode(query.opcode())
+    response.question = list(query.question)
+    if query.edns >= 0:
+        response.use_edns(0, 0, our_payload, query.payload)
+    if query.had_tsig:
+        response.use_tsig(query.keyring, query.keyname, fudge, None, 0, b'',
+                          query.keyalgorithm)
+        response.request_mac = query.mac
+    return response

+ 55 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/message.pyi

@@ -0,0 +1,55 @@
+from typing import Optional, Dict, List, Tuple, Union
+from . import name, rrset, tsig, rdatatype, entropy, edns, rdataclass
+import hmac
+
+class Message:
+    def to_wire(self, origin : Optional[name.Name]=None, max_size=0, **kw) -> bytes:
+        ...
+    def find_rrset(self, section : List[rrset.RRset], name : name.Name, rdclass : int, rdtype : int,
+                   covers=rdatatype.NONE, deleting : Optional[int]=None, create=False,
+                   force_unique=False) -> rrset.RRset:
+        ...
+    def __init__(self, id : Optional[int] =None) -> None:
+        self.id : int
+        self.flags = 0
+        self.question : List[rrset.RRset] = []
+        self.answer : List[rrset.RRset] = []
+        self.authority : List[rrset.RRset] = []
+        self.additional : List[rrset.RRset] = []
+        self.edns = -1
+        self.ednsflags = 0
+        self.payload = 0
+        self.options : List[edns.Option] = []
+        self.request_payload = 0
+        self.keyring = None
+        self.keyname = None
+        self.keyalgorithm = tsig.default_algorithm
+        self.request_mac = b''
+        self.other_data = b''
+        self.tsig_error = 0
+        self.fudge = 300
+        self.original_id = self.id
+        self.mac = b''
+        self.xfr = False
+        self.origin = None
+        self.tsig_ctx = None
+        self.had_tsig = False
+        self.multi = False
+        self.first = True
+        self.index : Dict[Tuple[rrset.RRset, name.Name, int, int, Union[int,str], int], rrset.RRset] = {}
+def from_text(a : str) -> Message:
+    ...
+
+def from_wire(wire, keyring : Optional[Dict[name.Name,bytes]] = None, request_mac = b'', xfr=False, origin=None,
+              tsig_ctx : Optional[hmac.HMAC] = None, multi=False, first=True,
+              question_only=False, one_rr_per_rrset=False,
+              ignore_trailing=False) -> Message:
+    ...
+def make_response(query : Message, recursion_available=False, our_payload=8192,
+                  fudge=300) -> Message:
+    ...
+
+def make_query(qname : Union[name.Name,str], rdtype : Union[str,int], rdclass : Union[int,str] =rdataclass.IN, use_edns : Optional[bool] = None,
+               want_dnssec=False, ednsflags : Optional[int] = None, payload : Optional[int] = None,
+               request_payload : Optional[int] = None, options : Optional[List[edns.Option]] = None) -> Message:
+    ...

+ 994 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/name.py

@@ -0,0 +1,994 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Names.
+"""
+
+from io import BytesIO
+import struct
+import sys
+import copy
+import encodings.idna
+try:
+    import idna
+    have_idna_2008 = True
+except ImportError:
+    have_idna_2008 = False
+
+import dns.exception
+import dns.wiredata
+
+from ._compat import long, binary_type, text_type, unichr, maybe_decode
+
+try:
+    maxint = sys.maxint  # pylint: disable=sys-max-int
+except AttributeError:
+    maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
+
+
+# fullcompare() result values
+
+#: The compared names have no relationship to each other.
+NAMERELN_NONE = 0
+#: the first name is a superdomain of the second.
+NAMERELN_SUPERDOMAIN = 1
+#: The first name is a subdomain of the second.
+NAMERELN_SUBDOMAIN = 2
+#: The compared names are equal.
+NAMERELN_EQUAL = 3
+#: The compared names have a common ancestor.
+NAMERELN_COMMONANCESTOR = 4
+
+
+class EmptyLabel(dns.exception.SyntaxError):
+    """A DNS label is empty."""
+
+
+class BadEscape(dns.exception.SyntaxError):
+    """An escaped code in a text format of DNS name is invalid."""
+
+
+class BadPointer(dns.exception.FormError):
+    """A DNS compression pointer points forward instead of backward."""
+
+
+class BadLabelType(dns.exception.FormError):
+    """The label type in DNS name wire format is unknown."""
+
+
+class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
+    """An attempt was made to convert a non-absolute name to
+    wire when there was also a non-absolute (or missing) origin."""
+
+
+class NameTooLong(dns.exception.FormError):
+    """A DNS name is > 255 octets long."""
+
+
+class LabelTooLong(dns.exception.SyntaxError):
+    """A DNS label is > 63 octets long."""
+
+
+class AbsoluteConcatenation(dns.exception.DNSException):
+    """An attempt was made to append anything other than the
+    empty name to an absolute DNS name."""
+
+
+class NoParent(dns.exception.DNSException):
+    """An attempt was made to get the parent of the root name
+    or the empty name."""
+
+class NoIDNA2008(dns.exception.DNSException):
+    """IDNA 2008 processing was requested but the idna module is not
+    available."""
+
+
+class IDNAException(dns.exception.DNSException):
+    """IDNA processing raised an exception."""
+
+    supp_kwargs = {'idna_exception'}
+    fmt = "IDNA processing exception: {idna_exception}"
+
+
+class IDNACodec(object):
+    """Abstract base class for IDNA encoder/decoders."""
+
+    def __init__(self):
+        pass
+
+    def encode(self, label):
+        raise NotImplementedError
+
+    def decode(self, label):
+        # We do not apply any IDNA policy on decode; we just
+        downcased = label.lower()
+        if downcased.startswith(b'xn--'):
+            try:
+                label = downcased[4:].decode('punycode')
+            except Exception as e:
+                raise IDNAException(idna_exception=e)
+        else:
+            label = maybe_decode(label)
+        return _escapify(label, True)
+
+
+class IDNA2003Codec(IDNACodec):
+    """IDNA 2003 encoder/decoder."""
+
+    def __init__(self, strict_decode=False):
+        """Initialize the IDNA 2003 encoder/decoder.
+
+        *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
+        is done when decoding.  This can cause failures if the name
+        was encoded with IDNA2008.  The default is `False`.
+        """
+
+        super(IDNA2003Codec, self).__init__()
+        self.strict_decode = strict_decode
+
+    def encode(self, label):
+        """Encode *label*."""
+
+        if label == '':
+            return b''
+        try:
+            return encodings.idna.ToASCII(label)
+        except UnicodeError:
+            raise LabelTooLong
+
+    def decode(self, label):
+        """Decode *label*."""
+        if not self.strict_decode:
+            return super(IDNA2003Codec, self).decode(label)
+        if label == b'':
+            return u''
+        try:
+            return _escapify(encodings.idna.ToUnicode(label), True)
+        except Exception as e:
+            raise IDNAException(idna_exception=e)
+
+
+class IDNA2008Codec(IDNACodec):
+    """IDNA 2008 encoder/decoder.
+
+        *uts_46* is a ``bool``.  If True, apply Unicode IDNA
+        compatibility processing as described in Unicode Technical
+        Standard #46 (http://unicode.org/reports/tr46/).
+        If False, do not apply the mapping.  The default is False.
+
+        *transitional* is a ``bool``: If True, use the
+        "transitional" mode described in Unicode Technical Standard
+        #46.  The default is False.
+
+        *allow_pure_ascii* is a ``bool``.  If True, then a label which
+        consists of only ASCII characters is allowed.  This is less
+        strict than regular IDNA 2008, but is also necessary for mixed
+        names, e.g. a name with starting with "_sip._tcp." and ending
+        in an IDN suffix which would otherwise be disallowed.  The
+        default is False.
+
+        *strict_decode* is a ``bool``: If True, then IDNA2008 checking
+        is done when decoding.  This can cause failures if the name
+        was encoded with IDNA2003.  The default is False.
+        """
+
+    def __init__(self, uts_46=False, transitional=False,
+                 allow_pure_ascii=False, strict_decode=False):
+        """Initialize the IDNA 2008 encoder/decoder."""
+        super(IDNA2008Codec, self).__init__()
+        self.uts_46 = uts_46
+        self.transitional = transitional
+        self.allow_pure_ascii = allow_pure_ascii
+        self.strict_decode = strict_decode
+
+    def is_all_ascii(self, label):
+        for c in label:
+            if ord(c) > 0x7f:
+                return False
+        return True
+
+    def encode(self, label):
+        if label == '':
+            return b''
+        if self.allow_pure_ascii and self.is_all_ascii(label):
+            return label.encode('ascii')
+        if not have_idna_2008:
+            raise NoIDNA2008
+        try:
+            if self.uts_46:
+                label = idna.uts46_remap(label, False, self.transitional)
+            return idna.alabel(label)
+        except idna.IDNAError as e:
+            raise IDNAException(idna_exception=e)
+
+    def decode(self, label):
+        if not self.strict_decode:
+            return super(IDNA2008Codec, self).decode(label)
+        if label == b'':
+            return u''
+        if not have_idna_2008:
+            raise NoIDNA2008
+        try:
+            if self.uts_46:
+                label = idna.uts46_remap(label, False, False)
+            return _escapify(idna.ulabel(label), True)
+        except idna.IDNAError as e:
+            raise IDNAException(idna_exception=e)
+
+_escaped = bytearray(b'"().;\\@$')
+
+IDNA_2003_Practical = IDNA2003Codec(False)
+IDNA_2003_Strict = IDNA2003Codec(True)
+IDNA_2003 = IDNA_2003_Practical
+IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)
+IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)
+IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)
+IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)
+IDNA_2008 = IDNA_2008_Practical
+
+def _escapify(label, unicode_mode=False):
+    """Escape the characters in label which need it.
+    @param unicode_mode: escapify only special and whitespace (<= 0x20)
+    characters
+    @returns: the escaped string
+    @rtype: string"""
+    if not unicode_mode:
+        text = ''
+        if isinstance(label, text_type):
+            label = label.encode()
+        for c in bytearray(label):
+            if c in _escaped:
+                text += '\\' + chr(c)
+            elif c > 0x20 and c < 0x7F:
+                text += chr(c)
+            else:
+                text += '\\%03d' % c
+        return text.encode()
+
+    text = u''
+    if isinstance(label, binary_type):
+        label = label.decode()
+    for c in label:
+        if c > u'\x20' and c < u'\x7f':
+            text += c
+        else:
+            if c >= u'\x7f':
+                text += c
+            else:
+                text += u'\\%03d' % ord(c)
+    return text
+
+def _validate_labels(labels):
+    """Check for empty labels in the middle of a label sequence,
+    labels that are too long, and for too many labels.
+
+    Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
+
+    Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root
+    label) and appears in a position other than the end of the label
+    sequence
+
+    """
+
+    l = len(labels)
+    total = 0
+    i = -1
+    j = 0
+    for label in labels:
+        ll = len(label)
+        total += ll + 1
+        if ll > 63:
+            raise LabelTooLong
+        if i < 0 and label == b'':
+            i = j
+        j += 1
+    if total > 255:
+        raise NameTooLong
+    if i >= 0 and i != l - 1:
+        raise EmptyLabel
+
+
+def _maybe_convert_to_binary(label):
+    """If label is ``text``, convert it to ``binary``.  If it is already
+    ``binary`` just return it.
+
+    """
+
+    if isinstance(label, binary_type):
+        return label
+    if isinstance(label, text_type):
+        return label.encode()
+    raise ValueError
+
+
+class Name(object):
+
+    """A DNS name.
+
+    The dns.name.Name class represents a DNS name as a tuple of
+    labels.  Each label is a `binary` in DNS wire format.  Instances
+    of the class are immutable.
+    """
+
+    __slots__ = ['labels']
+
+    def __init__(self, labels):
+        """*labels* is any iterable whose values are ``text`` or ``binary``.
+        """
+
+        labels = [_maybe_convert_to_binary(x) for x in labels]
+        super(Name, self).__setattr__('labels', tuple(labels))
+        _validate_labels(self.labels)
+
+    def __setattr__(self, name, value):
+        # Names are immutable
+        raise TypeError("object doesn't support attribute assignment")
+
+    def __copy__(self):
+        return Name(self.labels)
+
+    def __deepcopy__(self, memo):
+        return Name(copy.deepcopy(self.labels, memo))
+
+    def __getstate__(self):
+        # Names can be pickled
+        return {'labels': self.labels}
+
+    def __setstate__(self, state):
+        super(Name, self).__setattr__('labels', state['labels'])
+        _validate_labels(self.labels)
+
+    def is_absolute(self):
+        """Is the most significant label of this name the root label?
+
+        Returns a ``bool``.
+        """
+
+        return len(self.labels) > 0 and self.labels[-1] == b''
+
+    def is_wild(self):
+        """Is this name wild?  (I.e. Is the least significant label '*'?)
+
+        Returns a ``bool``.
+        """
+
+        return len(self.labels) > 0 and self.labels[0] == b'*'
+
+    def __hash__(self):
+        """Return a case-insensitive hash of the name.
+
+        Returns an ``int``.
+        """
+
+        h = long(0)
+        for label in self.labels:
+            for c in bytearray(label.lower()):
+                h += (h << 3) + c
+        return int(h % maxint)
+
+    def fullcompare(self, other):
+        """Compare two names, returning a 3-tuple
+        ``(relation, order, nlabels)``.
+
+        *relation* describes the relation ship between the names,
+        and is one of: ``dns.name.NAMERELN_NONE``,
+        ``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``,
+        ``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``.
+
+        *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
+        0 if *self* == *other*.  A relative name is always less than an
+        absolute name.  If both names have the same relativity, then
+        the DNSSEC order relation is used to order them.
+
+        *nlabels* is the number of significant labels that the two names
+        have in common.
+
+        Here are some examples.  Names ending in "." are absolute names,
+        those not ending in "." are relative names.
+
+        =============  =============  ===========  =====  =======
+        self           other          relation     order  nlabels
+        =============  =============  ===========  =====  =======
+        www.example.   www.example.   equal        0      3
+        www.example.   example.       subdomain    > 0    2
+        example.       www.example.   superdomain  < 0    2
+        example1.com.  example2.com.  common anc.  < 0    2
+        example1       example2.      none         < 0    0
+        example1.      example2       none         > 0    0
+        =============  =============  ===========  =====  =======
+        """
+
+        sabs = self.is_absolute()
+        oabs = other.is_absolute()
+        if sabs != oabs:
+            if sabs:
+                return (NAMERELN_NONE, 1, 0)
+            else:
+                return (NAMERELN_NONE, -1, 0)
+        l1 = len(self.labels)
+        l2 = len(other.labels)
+        ldiff = l1 - l2
+        if ldiff < 0:
+            l = l1
+        else:
+            l = l2
+
+        order = 0
+        nlabels = 0
+        namereln = NAMERELN_NONE
+        while l > 0:
+            l -= 1
+            l1 -= 1
+            l2 -= 1
+            label1 = self.labels[l1].lower()
+            label2 = other.labels[l2].lower()
+            if label1 < label2:
+                order = -1
+                if nlabels > 0:
+                    namereln = NAMERELN_COMMONANCESTOR
+                return (namereln, order, nlabels)
+            elif label1 > label2:
+                order = 1
+                if nlabels > 0:
+                    namereln = NAMERELN_COMMONANCESTOR
+                return (namereln, order, nlabels)
+            nlabels += 1
+        order = ldiff
+        if ldiff < 0:
+            namereln = NAMERELN_SUPERDOMAIN
+        elif ldiff > 0:
+            namereln = NAMERELN_SUBDOMAIN
+        else:
+            namereln = NAMERELN_EQUAL
+        return (namereln, order, nlabels)
+
+    def is_subdomain(self, other):
+        """Is self a subdomain of other?
+
+        Note that the notion of subdomain includes equality, e.g.
+        "dnpython.org" is a subdomain of itself.
+
+        Returns a ``bool``.
+        """
+
+        (nr, o, nl) = self.fullcompare(other)
+        if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
+            return True
+        return False
+
+    def is_superdomain(self, other):
+        """Is self a superdomain of other?
+
+        Note that the notion of superdomain includes equality, e.g.
+        "dnpython.org" is a superdomain of itself.
+
+        Returns a ``bool``.
+        """
+
+        (nr, o, nl) = self.fullcompare(other)
+        if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
+            return True
+        return False
+
+    def canonicalize(self):
+        """Return a name which is equal to the current name, but is in
+        DNSSEC canonical form.
+        """
+
+        return Name([x.lower() for x in self.labels])
+
+    def __eq__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] == 0
+        else:
+            return False
+
+    def __ne__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] != 0
+        else:
+            return True
+
+    def __lt__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] < 0
+        else:
+            return NotImplemented
+
+    def __le__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] <= 0
+        else:
+            return NotImplemented
+
+    def __ge__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] >= 0
+        else:
+            return NotImplemented
+
+    def __gt__(self, other):
+        if isinstance(other, Name):
+            return self.fullcompare(other)[1] > 0
+        else:
+            return NotImplemented
+
+    def __repr__(self):
+        return '<DNS name ' + self.__str__() + '>'
+
+    def __str__(self):
+        return self.to_text(False)
+
+    def to_text(self, omit_final_dot=False):
+        """Convert name to DNS text format.
+
+        *omit_final_dot* is a ``bool``.  If True, don't emit the final
+        dot (denoting the root label) for absolute names.  The default
+        is False.
+
+        Returns a ``text``.
+        """
+
+        if len(self.labels) == 0:
+            return maybe_decode(b'@')
+        if len(self.labels) == 1 and self.labels[0] == b'':
+            return maybe_decode(b'.')
+        if omit_final_dot and self.is_absolute():
+            l = self.labels[:-1]
+        else:
+            l = self.labels
+        s = b'.'.join(map(_escapify, l))
+        return maybe_decode(s)
+
+    def to_unicode(self, omit_final_dot=False, idna_codec=None):
+        """Convert name to Unicode text format.
+
+        IDN ACE labels are converted to Unicode.
+
+        *omit_final_dot* is a ``bool``.  If True, don't emit the final
+        dot (denoting the root label) for absolute names.  The default
+        is False.
+        *idna_codec* specifies the IDNA encoder/decoder.  If None, the
+        dns.name.IDNA_2003_Practical encoder/decoder is used.
+        The IDNA_2003_Practical decoder does
+        not impose any policy, it just decodes punycode, so if you
+        don't want checking for compliance, you can use this decoder
+        for IDNA2008 as well.
+
+        Returns a ``text``.
+        """
+
+        if len(self.labels) == 0:
+            return u'@'
+        if len(self.labels) == 1 and self.labels[0] == b'':
+            return u'.'
+        if omit_final_dot and self.is_absolute():
+            l = self.labels[:-1]
+        else:
+            l = self.labels
+        if idna_codec is None:
+            idna_codec = IDNA_2003_Practical
+        return u'.'.join([idna_codec.decode(x) for x in l])
+
+    def to_digestable(self, origin=None):
+        """Convert name to a format suitable for digesting in hashes.
+
+        The name is canonicalized and converted to uncompressed wire
+        format.  All names in wire format are absolute.  If the name
+        is a relative name, then an origin must be supplied.
+
+        *origin* is a ``dns.name.Name`` or ``None``.  If the name is
+        relative and origin is not ``None``, then origin will be appended
+        to the name.
+
+        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
+        relative and no origin was provided.
+
+        Returns a ``binary``.
+        """
+
+        if not self.is_absolute():
+            if origin is None or not origin.is_absolute():
+                raise NeedAbsoluteNameOrOrigin
+            labels = list(self.labels)
+            labels.extend(list(origin.labels))
+        else:
+            labels = self.labels
+        dlabels = [struct.pack('!B%ds' % len(x), len(x), x.lower())
+                   for x in labels]
+        return b''.join(dlabels)
+
+    def to_wire(self, file=None, compress=None, origin=None):
+        """Convert name to wire format, possibly compressing it.
+
+        *file* is the file where the name is emitted (typically a
+        BytesIO file).  If ``None`` (the default), a ``binary``
+        containing the wire name will be returned.
+
+        *compress*, a ``dict``, is the compression table to use.  If
+        ``None`` (the default), names will not be compressed.
+
+        *origin* is a ``dns.name.Name`` or ``None``.  If the name is
+        relative and origin is not ``None``, then *origin* will be appended
+        to it.
+
+        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
+        relative and no origin was provided.
+
+        Returns a ``binary`` or ``None``.
+        """
+
+        if file is None:
+            file = BytesIO()
+            want_return = True
+        else:
+            want_return = False
+
+        if not self.is_absolute():
+            if origin is None or not origin.is_absolute():
+                raise NeedAbsoluteNameOrOrigin
+            labels = list(self.labels)
+            labels.extend(list(origin.labels))
+        else:
+            labels = self.labels
+        i = 0
+        for label in labels:
+            n = Name(labels[i:])
+            i += 1
+            if compress is not None:
+                pos = compress.get(n)
+            else:
+                pos = None
+            if pos is not None:
+                value = 0xc000 + pos
+                s = struct.pack('!H', value)
+                file.write(s)
+                break
+            else:
+                if compress is not None and len(n) > 1:
+                    pos = file.tell()
+                    if pos <= 0x3fff:
+                        compress[n] = pos
+                l = len(label)
+                file.write(struct.pack('!B', l))
+                if l > 0:
+                    file.write(label)
+        if want_return:
+            return file.getvalue()
+
+    def __len__(self):
+        """The length of the name (in labels).
+
+        Returns an ``int``.
+        """
+
+        return len(self.labels)
+
+    def __getitem__(self, index):
+        return self.labels[index]
+
+    def __add__(self, other):
+        return self.concatenate(other)
+
+    def __sub__(self, other):
+        return self.relativize(other)
+
+    def split(self, depth):
+        """Split a name into a prefix and suffix names at the specified depth.
+
+        *depth* is an ``int`` specifying the number of labels in the suffix
+
+        Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
+        name.
+
+        Returns the tuple ``(prefix, suffix)``.
+        """
+
+        l = len(self.labels)
+        if depth == 0:
+            return (self, dns.name.empty)
+        elif depth == l:
+            return (dns.name.empty, self)
+        elif depth < 0 or depth > l:
+            raise ValueError(
+                'depth must be >= 0 and <= the length of the name')
+        return (Name(self[: -depth]), Name(self[-depth:]))
+
+    def concatenate(self, other):
+        """Return a new name which is the concatenation of self and other.
+
+        Raises ``dns.name.AbsoluteConcatenation`` if the name is
+        absolute and *other* is not the empty name.
+
+        Returns a ``dns.name.Name``.
+        """
+
+        if self.is_absolute() and len(other) > 0:
+            raise AbsoluteConcatenation
+        labels = list(self.labels)
+        labels.extend(list(other.labels))
+        return Name(labels)
+
+    def relativize(self, origin):
+        """If the name is a subdomain of *origin*, return a new name which is
+        the name relative to origin.  Otherwise return the name.
+
+        For example, relativizing ``www.dnspython.org.`` to origin
+        ``dnspython.org.`` returns the name ``www``.  Relativizing ``example.``
+        to origin ``dnspython.org.`` returns ``example.``.
+
+        Returns a ``dns.name.Name``.
+        """
+
+        if origin is not None and self.is_subdomain(origin):
+            return Name(self[: -len(origin)])
+        else:
+            return self
+
+    def derelativize(self, origin):
+        """If the name is a relative name, return a new name which is the
+        concatenation of the name and origin.  Otherwise return the name.
+
+        For example, derelativizing ``www`` to origin ``dnspython.org.``
+        returns the name ``www.dnspython.org.``.  Derelativizing ``example.``
+        to origin ``dnspython.org.`` returns ``example.``.
+
+        Returns a ``dns.name.Name``.
+        """
+
+        if not self.is_absolute():
+            return self.concatenate(origin)
+        else:
+            return self
+
+    def choose_relativity(self, origin=None, relativize=True):
+        """Return a name with the relativity desired by the caller.
+
+        If *origin* is ``None``, then the name is returned.
+        Otherwise, if *relativize* is ``True`` the name is
+        relativized, and if *relativize* is ``False`` the name is
+        derelativized.
+
+        Returns a ``dns.name.Name``.
+        """
+
+        if origin:
+            if relativize:
+                return self.relativize(origin)
+            else:
+                return self.derelativize(origin)
+        else:
+            return self
+
+    def parent(self):
+        """Return the parent of the name.
+
+        For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
+
+        Raises ``dns.name.NoParent`` if the name is either the root name or the
+        empty name, and thus has no parent.
+
+        Returns a ``dns.name.Name``.
+        """
+
+        if self == root or self == empty:
+            raise NoParent
+        return Name(self.labels[1:])
+
+#: The root name, '.'
+root = Name([b''])
+
+#: The empty name.
+empty = Name([])
+
+def from_unicode(text, origin=root, idna_codec=None):
+    """Convert unicode text into a Name object.
+
+    Labels are encoded in IDN ACE form according to rules specified by
+    the IDNA codec.
+
+    *text*, a ``text``, is the text to convert into a name.
+
+    *origin*, a ``dns.name.Name``, specifies the origin to
+    append to non-absolute names.  The default is the root name.
+
+    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
+    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder
+    is used.
+
+    Returns a ``dns.name.Name``.
+    """
+
+    if not isinstance(text, text_type):
+        raise ValueError("input to from_unicode() must be a unicode string")
+    if not (origin is None or isinstance(origin, Name)):
+        raise ValueError("origin must be a Name or None")
+    labels = []
+    label = u''
+    escaping = False
+    edigits = 0
+    total = 0
+    if idna_codec is None:
+        idna_codec = IDNA_2003
+    if text == u'@':
+        text = u''
+    if text:
+        if text == u'.':
+            return Name([b''])        # no Unicode "u" on this constant!
+        for c in text:
+            if escaping:
+                if edigits == 0:
+                    if c.isdigit():
+                        total = int(c)
+                        edigits += 1
+                    else:
+                        label += c
+                        escaping = False
+                else:
+                    if not c.isdigit():
+                        raise BadEscape
+                    total *= 10
+                    total += int(c)
+                    edigits += 1
+                    if edigits == 3:
+                        escaping = False
+                        label += unichr(total)
+            elif c in [u'.', u'\u3002', u'\uff0e', u'\uff61']:
+                if len(label) == 0:
+                    raise EmptyLabel
+                labels.append(idna_codec.encode(label))
+                label = u''
+            elif c == u'\\':
+                escaping = True
+                edigits = 0
+                total = 0
+            else:
+                label += c
+        if escaping:
+            raise BadEscape
+        if len(label) > 0:
+            labels.append(idna_codec.encode(label))
+        else:
+            labels.append(b'')
+
+    if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
+        labels.extend(list(origin.labels))
+    return Name(labels)
+
+
+def from_text(text, origin=root, idna_codec=None):
+    """Convert text into a Name object.
+
+    *text*, a ``text``, is the text to convert into a name.
+
+    *origin*, a ``dns.name.Name``, specifies the origin to
+    append to non-absolute names.  The default is the root name.
+
+    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
+    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder
+    is used.
+
+    Returns a ``dns.name.Name``.
+    """
+
+    if isinstance(text, text_type):
+        return from_unicode(text, origin, idna_codec)
+    if not isinstance(text, binary_type):
+        raise ValueError("input to from_text() must be a string")
+    if not (origin is None or isinstance(origin, Name)):
+        raise ValueError("origin must be a Name or None")
+    labels = []
+    label = b''
+    escaping = False
+    edigits = 0
+    total = 0
+    if text == b'@':
+        text = b''
+    if text:
+        if text == b'.':
+            return Name([b''])
+        for c in bytearray(text):
+            byte_ = struct.pack('!B', c)
+            if escaping:
+                if edigits == 0:
+                    if byte_.isdigit():
+                        total = int(byte_)
+                        edigits += 1
+                    else:
+                        label += byte_
+                        escaping = False
+                else:
+                    if not byte_.isdigit():
+                        raise BadEscape
+                    total *= 10
+                    total += int(byte_)
+                    edigits += 1
+                    if edigits == 3:
+                        escaping = False
+                        label += struct.pack('!B', total)
+            elif byte_ == b'.':
+                if len(label) == 0:
+                    raise EmptyLabel
+                labels.append(label)
+                label = b''
+            elif byte_ == b'\\':
+                escaping = True
+                edigits = 0
+                total = 0
+            else:
+                label += byte_
+        if escaping:
+            raise BadEscape
+        if len(label) > 0:
+            labels.append(label)
+        else:
+            labels.append(b'')
+    if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
+        labels.extend(list(origin.labels))
+    return Name(labels)
+
+
+def from_wire(message, current):
+    """Convert possibly compressed wire format into a Name.
+
+    *message* is a ``binary`` containing an entire DNS message in DNS
+    wire form.
+
+    *current*, an ``int``, is the offset of the beginning of the name
+    from the start of the message
+
+    Raises ``dns.name.BadPointer`` if a compression pointer did not
+    point backwards in the message.
+
+    Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
+
+    Returns a ``(dns.name.Name, int)`` tuple consisting of the name
+    that was read and the number of bytes of the wire format message
+    which were consumed reading it.
+    """
+
+    if not isinstance(message, binary_type):
+        raise ValueError("input to from_wire() must be a byte string")
+    message = dns.wiredata.maybe_wrap(message)
+    labels = []
+    biggest_pointer = current
+    hops = 0
+    count = message[current]
+    current += 1
+    cused = 1
+    while count != 0:
+        if count < 64:
+            labels.append(message[current: current + count].unwrap())
+            current += count
+            if hops == 0:
+                cused += count
+        elif count >= 192:
+            current = (count & 0x3f) * 256 + message[current]
+            if hops == 0:
+                cused += 1
+            if current >= biggest_pointer:
+                raise BadPointer
+            biggest_pointer = current
+            hops += 1
+        else:
+            raise BadLabelType
+        count = message[current]
+        current += 1
+        if hops == 0:
+            cused += 1
+    labels.append('')
+    return (Name(labels), cused)

+ 35 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/name.pyi

@@ -0,0 +1,35 @@
+from typing import Optional, Union, Tuple, Iterable, List
+
+class Name:
+    def is_subdomain(self, o : Name) -> bool: ...
+    def is_superdomain(self, o : Name) -> bool: ...
+    def __init__(self, labels : Iterable[Union[bytes,str]]) -> None:
+        self.labels : List[bytes]
+    def is_absolute(self) -> bool: ...
+    def is_wild(self) -> bool: ...
+    def fullcompare(self, other) -> Tuple[int,int,int]: ...
+    def canonicalize(self) -> Name: ...
+    def __lt__(self, other : Name): ...
+    def __le__(self, other : Name): ...
+    def __ge__(self, other : Name): ...
+    def __gt__(self, other : Name): ...
+    def to_text(self, omit_final_dot=False) -> str: ...
+    def to_unicode(self, omit_final_dot=False, idna_codec=None) -> str: ...
+    def to_digestable(self, origin=None) -> bytes: ...
+    def to_wire(self, file=None, compress=None, origin=None) -> Optional[bytes]: ...
+    def __add__(self, other : Name): ...
+    def __sub__(self, other : Name): ...
+    def split(self, depth) -> List[Tuple[str,str]]: ...
+    def concatenate(self, other : Name) -> Name: ...
+    def relativize(self, origin): ...
+    def derelativize(self, origin): ...
+    def choose_relativity(self, origin : Optional[Name] = None, relativize=True): ...
+    def parent(self) -> Name: ...
+
+class IDNACodec:
+    pass
+
+def from_text(text, origin : Optional[Name] = Name('.'), idna_codec : Optional[IDNACodec] = None) -> Name:
+    ...
+
+empty : Name

+ 108 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/namedict.py

@@ -0,0 +1,108 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+# Copyright (C) 2016 Coresec Systems AB
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC
+# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS name dictionary"""
+
+from collections.abc import MutableMapping
+import dns.name
+from ._compat import xrange
+
+
+class NameDict(MutableMapping):
+    """A dictionary whose keys are dns.name.Name objects.
+
+    In addition to being like a regular Python dictionary, this
+    dictionary can also get the deepest match for a given key.
+    """
+
+    __slots__ = ["max_depth", "max_depth_items", "__store"]
+
+    def __init__(self, *args, **kwargs):
+        super(NameDict, self).__init__()
+        self.__store = dict()
+        #: the maximum depth of the keys that have ever been added
+        self.max_depth = 0
+        #: the number of items of maximum depth
+        self.max_depth_items = 0
+        self.update(dict(*args, **kwargs))
+
+    def __update_max_depth(self, key):
+        if len(key) == self.max_depth:
+            self.max_depth_items = self.max_depth_items + 1
+        elif len(key) > self.max_depth:
+            self.max_depth = len(key)
+            self.max_depth_items = 1
+
+    def __getitem__(self, key):
+        return self.__store[key]
+
+    def __setitem__(self, key, value):
+        if not isinstance(key, dns.name.Name):
+            raise ValueError('NameDict key must be a name')
+        self.__store[key] = value
+        self.__update_max_depth(key)
+
+    def __delitem__(self, key):
+        value = self.__store.pop(key)
+        if len(value) == self.max_depth:
+            self.max_depth_items = self.max_depth_items - 1
+        if self.max_depth_items == 0:
+            self.max_depth = 0
+            for k in self.__store:
+                self.__update_max_depth(k)
+
+    def __iter__(self):
+        return iter(self.__store)
+
+    def __len__(self):
+        return len(self.__store)
+
+    def has_key(self, key):
+        return key in self.__store
+
+    def get_deepest_match(self, name):
+        """Find the deepest match to *fname* in the dictionary.
+
+        The deepest match is the longest name in the dictionary which is
+        a superdomain of *name*.  Note that *superdomain* includes matching
+        *name* itself.
+
+        *name*, a ``dns.name.Name``, the name to find.
+
+        Returns a ``(key, value)`` where *key* is the deepest
+        ``dns.name.Name``, and *value* is the value associated with *key*.
+        """
+
+        depth = len(name)
+        if depth > self.max_depth:
+            depth = self.max_depth
+        for i in xrange(-depth, 0):
+            n = dns.name.Name(name[i:])
+            if n in self:
+                return (n, self[n])
+        v = self[dns.name.empty]
+        return (dns.name.empty, v)

+ 182 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/node.py

@@ -0,0 +1,182 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS nodes.  A node is a set of rdatasets."""
+
+from io import StringIO
+
+import dns.rdataset
+import dns.rdatatype
+import dns.renderer
+
+
+class Node(object):
+
+    """A Node is a set of rdatasets."""
+
+    __slots__ = ['rdatasets']
+
+    def __init__(self):
+        #: the set of rdatsets, represented as a list.
+        self.rdatasets = []
+
+    def to_text(self, name, **kw):
+        """Convert a node to text format.
+
+        Each rdataset at the node is printed.  Any keyword arguments
+        to this method are passed on to the rdataset's to_text() method.
+
+        *name*, a ``dns.name.Name`` or ``text``, the owner name of the rdatasets.
+
+        Returns a ``text``.
+        """
+
+        s = StringIO()
+        for rds in self.rdatasets:
+            if len(rds) > 0:
+                s.write(rds.to_text(name, **kw))
+                s.write(u'\n')
+        return s.getvalue()[:-1]
+
+    def __repr__(self):
+        return '<DNS node ' + str(id(self)) + '>'
+
+    def __eq__(self, other):
+        #
+        # This is inefficient.  Good thing we don't need to do it much.
+        #
+        for rd in self.rdatasets:
+            if rd not in other.rdatasets:
+                return False
+        for rd in other.rdatasets:
+            if rd not in self.rdatasets:
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __len__(self):
+        return len(self.rdatasets)
+
+    def __iter__(self):
+        return iter(self.rdatasets)
+
+    def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
+                      create=False):
+        """Find an rdataset matching the specified properties in the
+        current node.
+
+        *rdclass*, an ``int``, the class of the rdataset.
+
+        *rdtype*, an ``int``, the type of the rdataset.
+
+        *covers*, an ``int``, the covered type.  Usually this value is
+        dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
+        dns.rdatatype.RRSIG, then the covers value will be the rdata
+        type the SIG/RRSIG covers.  The library treats the SIG and RRSIG
+        types as if they were a family of
+        types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA).  This makes RRSIGs much
+        easier to work with than if RRSIGs covering different rdata
+        types were aggregated into a single RRSIG rdataset.
+
+        *create*, a ``bool``.  If True, create the rdataset if it is not found.
+
+        Raises ``KeyError`` if an rdataset of the desired type and class does
+        not exist and *create* is not ``True``.
+
+        Returns a ``dns.rdataset.Rdataset``.
+        """
+
+        for rds in self.rdatasets:
+            if rds.match(rdclass, rdtype, covers):
+                return rds
+        if not create:
+            raise KeyError
+        rds = dns.rdataset.Rdataset(rdclass, rdtype)
+        self.rdatasets.append(rds)
+        return rds
+
+    def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
+                     create=False):
+        """Get an rdataset matching the specified properties in the
+        current node.
+
+        None is returned if an rdataset of the specified type and
+        class does not exist and *create* is not ``True``.
+
+        *rdclass*, an ``int``, the class of the rdataset.
+
+        *rdtype*, an ``int``, the type of the rdataset.
+
+        *covers*, an ``int``, the covered type.  Usually this value is
+        dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
+        dns.rdatatype.RRSIG, then the covers value will be the rdata
+        type the SIG/RRSIG covers.  The library treats the SIG and RRSIG
+        types as if they were a family of
+        types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA).  This makes RRSIGs much
+        easier to work with than if RRSIGs covering different rdata
+        types were aggregated into a single RRSIG rdataset.
+
+        *create*, a ``bool``.  If True, create the rdataset if it is not found.
+
+        Returns a ``dns.rdataset.Rdataset`` or ``None``.
+        """
+
+        try:
+            rds = self.find_rdataset(rdclass, rdtype, covers, create)
+        except KeyError:
+            rds = None
+        return rds
+
+    def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
+        """Delete the rdataset matching the specified properties in the
+        current node.
+
+        If a matching rdataset does not exist, it is not an error.
+
+        *rdclass*, an ``int``, the class of the rdataset.
+
+        *rdtype*, an ``int``, the type of the rdataset.
+
+        *covers*, an ``int``, the covered type.
+        """
+
+        rds = self.get_rdataset(rdclass, rdtype, covers)
+        if rds is not None:
+            self.rdatasets.remove(rds)
+
+    def replace_rdataset(self, replacement):
+        """Replace an rdataset.
+
+        It is not an error if there is no rdataset matching *replacement*.
+
+        Ownership of the *replacement* object is transferred to the node;
+        in other words, this method does not store a copy of *replacement*
+        at the node, it stores *replacement* itself.
+
+        *replacement*, a ``dns.rdataset.Rdataset``.
+
+        Raises ``ValueError`` if *replacement* is not a
+        ``dns.rdataset.Rdataset``.
+        """
+
+        if not isinstance(replacement, dns.rdataset.Rdataset):
+            raise ValueError('replacement is not an rdataset')
+        self.delete_rdataset(replacement.rdclass, replacement.rdtype,
+                             replacement.covers)
+        self.rdatasets.append(replacement)

+ 17 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/node.pyi

@@ -0,0 +1,17 @@
+from typing import List, Optional, Union
+from . import rdataset, rdatatype, name
+class Node:
+    def __init__(self):
+        self.rdatasets : List[rdataset.Rdataset]
+    def to_text(self, name : Union[str,name.Name], **kw) -> str:
+        ...
+    def find_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE,
+                      create=False) -> rdataset.Rdataset:
+        ...
+    def get_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE,
+                     create=False) -> Optional[rdataset.Rdataset]:
+        ...
+    def delete_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE):
+        ...
+    def replace_rdataset(self, replacement : rdataset.Rdataset) -> None:
+        ...

+ 119 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/opcode.py

@@ -0,0 +1,119 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Opcodes."""
+
+import dns.exception
+
+#: Query
+QUERY = 0
+#: Inverse Query (historical)
+IQUERY = 1
+#: Server Status (unspecified and unimplemented anywhere)
+STATUS = 2
+#: Notify
+NOTIFY = 4
+#: Dynamic Update
+UPDATE = 5
+
+_by_text = {
+    'QUERY': QUERY,
+    'IQUERY': IQUERY,
+    'STATUS': STATUS,
+    'NOTIFY': NOTIFY,
+    'UPDATE': UPDATE
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = {y: x for x, y in _by_text.items()}
+
+
+class UnknownOpcode(dns.exception.DNSException):
+    """An DNS opcode is unknown."""
+
+
+def from_text(text):
+    """Convert text into an opcode.
+
+    *text*, a ``text``, the textual opcode
+
+    Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
+
+    Returns an ``int``.
+    """
+
+    if text.isdigit():
+        value = int(text)
+        if value >= 0 and value <= 15:
+            return value
+    value = _by_text.get(text.upper())
+    if value is None:
+        raise UnknownOpcode
+    return value
+
+
+def from_flags(flags):
+    """Extract an opcode from DNS message flags.
+
+    *flags*, an ``int``, the DNS flags.
+
+    Returns an ``int``.
+    """
+
+    return (flags & 0x7800) >> 11
+
+
+def to_flags(value):
+    """Convert an opcode to a value suitable for ORing into DNS message
+    flags.
+
+    *value*, an ``int``, the DNS opcode value.
+
+    Returns an ``int``.
+    """
+
+    return (value << 11) & 0x7800
+
+
+def to_text(value):
+    """Convert an opcode to text.
+
+    *value*, an ``int`` the opcode value,
+
+    Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
+
+    Returns a ``text``.
+    """
+
+    text = _by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text
+
+
+def is_update(flags):
+    """Is the opcode in flags UPDATE?
+
+    *flags*, an ``int``, the DNS message flags.
+
+    Returns a ``bool``.
+    """
+
+    return from_flags(flags) == UPDATE

+ 0 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/py.typed


+ 683 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/query.py

@@ -0,0 +1,683 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Talk to a DNS server."""
+
+from __future__ import generators
+
+import errno
+import select
+import socket
+import struct
+import sys
+import time
+
+import dns.exception
+import dns.inet
+import dns.name
+import dns.message
+import dns.rcode
+import dns.rdataclass
+import dns.rdatatype
+from ._compat import long, string_types, PY3
+
+if PY3:
+    select_error = OSError
+else:
+    select_error = select.error
+
+# Function used to create a socket.  Can be overridden if needed in special
+# situations.
+socket_factory = socket.socket
+
+class UnexpectedSource(dns.exception.DNSException):
+    """A DNS query response came from an unexpected address or port."""
+
+
+class BadResponse(dns.exception.FormError):
+    """A DNS query response does not respond to the question asked."""
+
+
+class TransferError(dns.exception.DNSException):
+    """A zone transfer response got a non-zero rcode."""
+
+    def __init__(self, rcode):
+        message = 'Zone transfer error: %s' % dns.rcode.to_text(rcode)
+        super(TransferError, self).__init__(message)
+        self.rcode = rcode
+
+
+def _compute_expiration(timeout):
+    if timeout is None:
+        return None
+    else:
+        return time.time() + timeout
+
+# This module can use either poll() or select() as the "polling backend".
+#
+# A backend function takes an fd, bools for readability, writablity, and
+# error detection, and a timeout.
+
+def _poll_for(fd, readable, writable, error, timeout):
+    """Poll polling backend."""
+
+    event_mask = 0
+    if readable:
+        event_mask |= select.POLLIN
+    if writable:
+        event_mask |= select.POLLOUT
+    if error:
+        event_mask |= select.POLLERR
+
+    pollable = select.poll()
+    pollable.register(fd, event_mask)
+
+    if timeout:
+        event_list = pollable.poll(long(timeout * 1000))
+    else:
+        event_list = pollable.poll()
+
+    return bool(event_list)
+
+
+def _select_for(fd, readable, writable, error, timeout):
+    """Select polling backend."""
+
+    rset, wset, xset = [], [], []
+
+    if readable:
+        rset = [fd]
+    if writable:
+        wset = [fd]
+    if error:
+        xset = [fd]
+
+    if timeout is None:
+        (rcount, wcount, xcount) = select.select(rset, wset, xset)
+    else:
+        (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout)
+
+    return bool((rcount or wcount or xcount))
+
+
+def _wait_for(fd, readable, writable, error, expiration):
+    # Use the selected polling backend to wait for any of the specified
+    # events.  An "expiration" absolute time is converted into a relative
+    # timeout.
+
+    done = False
+    while not done:
+        if expiration is None:
+            timeout = None
+        else:
+            timeout = expiration - time.time()
+            if timeout <= 0.0:
+                raise dns.exception.Timeout
+        try:
+            if not _polling_backend(fd, readable, writable, error, timeout):
+                raise dns.exception.Timeout
+        except select_error as e:
+            if e.args[0] != errno.EINTR:
+                raise e
+        done = True
+
+
+def _set_polling_backend(fn):
+    # Internal API. Do not use.
+
+    global _polling_backend
+
+    _polling_backend = fn
+
+if hasattr(select, 'poll'):
+    # Prefer poll() on platforms that support it because it has no
+    # limits on the maximum value of a file descriptor (plus it will
+    # be more efficient for high values).
+    _polling_backend = _poll_for
+else:
+    _polling_backend = _select_for
+
+
+def _wait_for_readable(s, expiration):
+    _wait_for(s, True, False, True, expiration)
+
+
+def _wait_for_writable(s, expiration):
+    _wait_for(s, False, True, True, expiration)
+
+
+def _addresses_equal(af, a1, a2):
+    # Convert the first value of the tuple, which is a textual format
+    # address into binary form, so that we are not confused by different
+    # textual representations of the same address
+    try:
+        n1 = dns.inet.inet_pton(af, a1[0])
+        n2 = dns.inet.inet_pton(af, a2[0])
+    except dns.exception.SyntaxError:
+        return False
+    return n1 == n2 and a1[1:] == a2[1:]
+
+
+def _destination_and_source(af, where, port, source, source_port):
+    # Apply defaults and compute destination and source tuples
+    # suitable for use in connect(), sendto(), or bind().
+    if af is None:
+        try:
+            af = dns.inet.af_for_address(where)
+        except Exception:
+            af = dns.inet.AF_INET
+    if af == dns.inet.AF_INET:
+        destination = (where, port)
+        if source is not None or source_port != 0:
+            if source is None:
+                source = '0.0.0.0'
+            source = (source, source_port)
+    elif af == dns.inet.AF_INET6:
+        destination = (where, port, 0, 0)
+        if source is not None or source_port != 0:
+            if source is None:
+                source = '::'
+            source = (source, source_port, 0, 0)
+    return (af, destination, source)
+
+
+def send_udp(sock, what, destination, expiration=None):
+    """Send a DNS message to the specified UDP socket.
+
+    *sock*, a ``socket``.
+
+    *what*, a ``binary`` or ``dns.message.Message``, the message to send.
+
+    *destination*, a destination tuple appropriate for the address family
+    of the socket, specifying where to send the query.
+
+    *expiration*, a ``float`` or ``None``, the absolute time at which
+    a timeout exception should be raised.  If ``None``, no timeout will
+    occur.
+
+    Returns an ``(int, float)`` tuple of bytes sent and the sent time.
+    """
+
+    if isinstance(what, dns.message.Message):
+        what = what.to_wire()
+    _wait_for_writable(sock, expiration)
+    sent_time = time.time()
+    n = sock.sendto(what, destination)
+    return (n, sent_time)
+
+
+def receive_udp(sock, destination, expiration=None,
+                ignore_unexpected=False, one_rr_per_rrset=False,
+                keyring=None, request_mac=b'', ignore_trailing=False):
+    """Read a DNS message from a UDP socket.
+
+    *sock*, a ``socket``.
+
+    *destination*, a destination tuple appropriate for the address family
+    of the socket, specifying where the associated query was sent.
+
+    *expiration*, a ``float`` or ``None``, the absolute time at which
+    a timeout exception should be raised.  If ``None``, no timeout will
+    occur.
+
+    *ignore_unexpected*, a ``bool``.  If ``True``, ignore responses from
+    unexpected sources.
+
+    *one_rr_per_rrset*, a ``bool``.  If ``True``, put each RR into its own
+    RRset.
+
+    *keyring*, a ``dict``, the keyring to use for TSIG.
+
+    *request_mac*, a ``binary``, the MAC of the request (for TSIG).
+
+    *ignore_trailing*, a ``bool``.  If ``True``, ignore trailing
+    junk at end of the received message.
+
+    Raises if the message is malformed, if network errors occur, of if
+    there is a timeout.
+
+    Returns a ``dns.message.Message`` object.
+    """
+
+    wire = b''
+    while 1:
+        _wait_for_readable(sock, expiration)
+        (wire, from_address) = sock.recvfrom(65535)
+        if _addresses_equal(sock.family, from_address, destination) or \
+           (dns.inet.is_multicast(destination[0]) and
+            from_address[1:] == destination[1:]):
+            break
+        if not ignore_unexpected:
+            raise UnexpectedSource('got a response from '
+                                   '%s instead of %s' % (from_address,
+                                                         destination))
+    received_time = time.time()
+    r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac,
+                              one_rr_per_rrset=one_rr_per_rrset,
+                              ignore_trailing=ignore_trailing)
+    return (r, received_time)
+
+def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
+        ignore_unexpected=False, one_rr_per_rrset=False, ignore_trailing=False):
+    """Return the response obtained after sending a query via UDP.
+
+    *q*, a ``dns.message.Message``, the query to send
+
+    *where*, a ``text`` containing an IPv4 or IPv6 address,  where
+    to send the message.
+
+    *timeout*, a ``float`` or ``None``, the number of seconds to wait before the
+    query times out.  If ``None``, the default, wait forever.
+
+    *port*, an ``int``, the port send the message to.  The default is 53.
+
+    *af*, an ``int``, the address family to use.  The default is ``None``,
+    which causes the address family to use to be inferred from the form of
+    *where*.  If the inference attempt fails, AF_INET is used.  This
+    parameter is historical; you need never set it.
+
+    *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+    the source address.  The default is the wildcard address.
+
+    *source_port*, an ``int``, the port from which to send the message.
+    The default is 0.
+
+    *ignore_unexpected*, a ``bool``.  If ``True``, ignore responses from
+    unexpected sources.
+
+    *one_rr_per_rrset*, a ``bool``.  If ``True``, put each RR into its own
+    RRset.
+
+    *ignore_trailing*, a ``bool``.  If ``True``, ignore trailing
+    junk at end of the received message.
+
+    Returns a ``dns.message.Message``.
+    """
+
+    wire = q.to_wire()
+    (af, destination, source) = _destination_and_source(af, where, port,
+                                                        source, source_port)
+    s = socket_factory(af, socket.SOCK_DGRAM, 0)
+    received_time = None
+    sent_time = None
+    try:
+        expiration = _compute_expiration(timeout)
+        s.setblocking(0)
+        if source is not None:
+            s.bind(source)
+        (_, sent_time) = send_udp(s, wire, destination, expiration)
+        (r, received_time) = receive_udp(s, destination, expiration,
+                                         ignore_unexpected, one_rr_per_rrset,
+                                         q.keyring, q.mac, ignore_trailing)
+    finally:
+        if sent_time is None or received_time is None:
+            response_time = 0
+        else:
+            response_time = received_time - sent_time
+        s.close()
+    r.time = response_time
+    if not q.is_response(r):
+        raise BadResponse
+    return r
+
+
+def _net_read(sock, count, expiration):
+    """Read the specified number of bytes from sock.  Keep trying until we
+    either get the desired amount, or we hit EOF.
+    A Timeout exception will be raised if the operation is not completed
+    by the expiration time.
+    """
+    s = b''
+    while count > 0:
+        _wait_for_readable(sock, expiration)
+        n = sock.recv(count)
+        if n == b'':
+            raise EOFError
+        count = count - len(n)
+        s = s + n
+    return s
+
+
+def _net_write(sock, data, expiration):
+    """Write the specified data to the socket.
+    A Timeout exception will be raised if the operation is not completed
+    by the expiration time.
+    """
+    current = 0
+    l = len(data)
+    while current < l:
+        _wait_for_writable(sock, expiration)
+        current += sock.send(data[current:])
+
+
+def send_tcp(sock, what, expiration=None):
+    """Send a DNS message to the specified TCP socket.
+
+    *sock*, a ``socket``.
+
+    *what*, a ``binary`` or ``dns.message.Message``, the message to send.
+
+    *expiration*, a ``float`` or ``None``, the absolute time at which
+    a timeout exception should be raised.  If ``None``, no timeout will
+    occur.
+
+    Returns an ``(int, float)`` tuple of bytes sent and the sent time.
+    """
+
+    if isinstance(what, dns.message.Message):
+        what = what.to_wire()
+    l = len(what)
+    # copying the wire into tcpmsg is inefficient, but lets us
+    # avoid writev() or doing a short write that would get pushed
+    # onto the net
+    tcpmsg = struct.pack("!H", l) + what
+    _wait_for_writable(sock, expiration)
+    sent_time = time.time()
+    _net_write(sock, tcpmsg, expiration)
+    return (len(tcpmsg), sent_time)
+
+def receive_tcp(sock, expiration=None, one_rr_per_rrset=False,
+                keyring=None, request_mac=b'', ignore_trailing=False):
+    """Read a DNS message from a TCP socket.
+
+    *sock*, a ``socket``.
+
+    *expiration*, a ``float`` or ``None``, the absolute time at which
+    a timeout exception should be raised.  If ``None``, no timeout will
+    occur.
+
+    *one_rr_per_rrset*, a ``bool``.  If ``True``, put each RR into its own
+    RRset.
+
+    *keyring*, a ``dict``, the keyring to use for TSIG.
+
+    *request_mac*, a ``binary``, the MAC of the request (for TSIG).
+
+    *ignore_trailing*, a ``bool``.  If ``True``, ignore trailing
+    junk at end of the received message.
+
+    Raises if the message is malformed, if network errors occur, of if
+    there is a timeout.
+
+    Returns a ``dns.message.Message`` object.
+    """
+
+    ldata = _net_read(sock, 2, expiration)
+    (l,) = struct.unpack("!H", ldata)
+    wire = _net_read(sock, l, expiration)
+    received_time = time.time()
+    r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac,
+                              one_rr_per_rrset=one_rr_per_rrset,
+                              ignore_trailing=ignore_trailing)
+    return (r, received_time)
+
+def _connect(s, address):
+    try:
+        s.connect(address)
+    except socket.error:
+        (ty, v) = sys.exc_info()[:2]
+
+        if hasattr(v, 'errno'):
+            v_err = v.errno
+        else:
+            v_err = v[0]
+        if v_err not in [errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY]:
+            raise v
+
+
+def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
+        one_rr_per_rrset=False, ignore_trailing=False):
+    """Return the response obtained after sending a query via TCP.
+
+    *q*, a ``dns.message.Message``, the query to send
+
+    *where*, a ``text`` containing an IPv4 or IPv6 address,  where
+    to send the message.
+
+    *timeout*, a ``float`` or ``None``, the number of seconds to wait before the
+    query times out.  If ``None``, the default, wait forever.
+
+    *port*, an ``int``, the port send the message to.  The default is 53.
+
+    *af*, an ``int``, the address family to use.  The default is ``None``,
+    which causes the address family to use to be inferred from the form of
+    *where*.  If the inference attempt fails, AF_INET is used.  This
+    parameter is historical; you need never set it.
+
+    *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+    the source address.  The default is the wildcard address.
+
+    *source_port*, an ``int``, the port from which to send the message.
+    The default is 0.
+
+    *one_rr_per_rrset*, a ``bool``.  If ``True``, put each RR into its own
+    RRset.
+
+    *ignore_trailing*, a ``bool``.  If ``True``, ignore trailing
+    junk at end of the received message.
+
+    Returns a ``dns.message.Message``.
+    """
+
+    wire = q.to_wire()
+    (af, destination, source) = _destination_and_source(af, where, port,
+                                                        source, source_port)
+    s = socket_factory(af, socket.SOCK_STREAM, 0)
+    begin_time = None
+    received_time = None
+    try:
+        expiration = _compute_expiration(timeout)
+        s.setblocking(0)
+        begin_time = time.time()
+        if source is not None:
+            s.bind(source)
+        _connect(s, destination)
+        send_tcp(s, wire, expiration)
+        (r, received_time) = receive_tcp(s, expiration, one_rr_per_rrset,
+                                         q.keyring, q.mac, ignore_trailing)
+    finally:
+        if begin_time is None or received_time is None:
+            response_time = 0
+        else:
+            response_time = received_time - begin_time
+        s.close()
+    r.time = response_time
+    if not q.is_response(r):
+        raise BadResponse
+    return r
+
+
+def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
+        timeout=None, port=53, keyring=None, keyname=None, relativize=True,
+        af=None, lifetime=None, source=None, source_port=0, serial=0,
+        use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
+    """Return a generator for the responses to a zone transfer.
+
+    *where*.  If the inference attempt fails, AF_INET is used.  This
+    parameter is historical; you need never set it.
+
+    *zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer.
+
+    *rdtype*, an ``int`` or ``text``, the type of zone transfer.  The
+    default is ``dns.rdatatype.AXFR``.  ``dns.rdatatype.IXFR`` can be
+    used to do an incremental transfer instead.
+
+    *rdclass*, an ``int`` or ``text``, the class of the zone transfer.
+    The default is ``dns.rdataclass.IN``.
+
+    *timeout*, a ``float``, the number of seconds to wait for each
+    response message.  If None, the default, wait forever.
+
+    *port*, an ``int``, the port send the message to.  The default is 53.
+
+    *keyring*, a ``dict``, the keyring to use for TSIG.
+
+    *keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG
+    key to use.
+
+    *relativize*, a ``bool``.  If ``True``, all names in the zone will be
+    relativized to the zone origin.  It is essential that the
+    relativize setting matches the one specified to
+    ``dns.zone.from_xfr()`` if using this generator to make a zone.
+
+    *af*, an ``int``, the address family to use.  The default is ``None``,
+    which causes the address family to use to be inferred from the form of
+    *where*.  If the inference attempt fails, AF_INET is used.  This
+    parameter is historical; you need never set it.
+
+    *lifetime*, a ``float``, the total number of seconds to spend
+    doing the transfer.  If ``None``, the default, then there is no
+    limit on the time the transfer may take.
+
+    *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+    the source address.  The default is the wildcard address.
+
+    *source_port*, an ``int``, the port from which to send the message.
+    The default is 0.
+
+    *serial*, an ``int``, the SOA serial number to use as the base for
+    an IXFR diff sequence (only meaningful if *rdtype* is
+    ``dns.rdatatype.IXFR``).
+
+    *use_udp*, a ``bool``.  If ``True``, use UDP (only meaningful for IXFR).
+
+    *keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use.
+
+    Raises on errors, and so does the generator.
+
+    Returns a generator of ``dns.message.Message`` objects.
+    """
+
+    if isinstance(zone, string_types):
+        zone = dns.name.from_text(zone)
+    if isinstance(rdtype, string_types):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    q = dns.message.make_query(zone, rdtype, rdclass)
+    if rdtype == dns.rdatatype.IXFR:
+        rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA',
+                                    '. . %u 0 0 0 0' % serial)
+        q.authority.append(rrset)
+    if keyring is not None:
+        q.use_tsig(keyring, keyname, algorithm=keyalgorithm)
+    wire = q.to_wire()
+    (af, destination, source) = _destination_and_source(af, where, port,
+                                                        source, source_port)
+    if use_udp:
+        if rdtype != dns.rdatatype.IXFR:
+            raise ValueError('cannot do a UDP AXFR')
+        s = socket_factory(af, socket.SOCK_DGRAM, 0)
+    else:
+        s = socket_factory(af, socket.SOCK_STREAM, 0)
+    s.setblocking(0)
+    if source is not None:
+        s.bind(source)
+    expiration = _compute_expiration(lifetime)
+    _connect(s, destination)
+    l = len(wire)
+    if use_udp:
+        _wait_for_writable(s, expiration)
+        s.send(wire)
+    else:
+        tcpmsg = struct.pack("!H", l) + wire
+        _net_write(s, tcpmsg, expiration)
+    done = False
+    delete_mode = True
+    expecting_SOA = False
+    soa_rrset = None
+    if relativize:
+        origin = zone
+        oname = dns.name.empty
+    else:
+        origin = None
+        oname = zone
+    tsig_ctx = None
+    first = True
+    while not done:
+        mexpiration = _compute_expiration(timeout)
+        if mexpiration is None or mexpiration > expiration:
+            mexpiration = expiration
+        if use_udp:
+            _wait_for_readable(s, expiration)
+            (wire, from_address) = s.recvfrom(65535)
+        else:
+            ldata = _net_read(s, 2, mexpiration)
+            (l,) = struct.unpack("!H", ldata)
+            wire = _net_read(s, l, mexpiration)
+        is_ixfr = (rdtype == dns.rdatatype.IXFR)
+        r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                                  xfr=True, origin=origin, tsig_ctx=tsig_ctx,
+                                  multi=True, first=first,
+                                  one_rr_per_rrset=is_ixfr)
+        rcode = r.rcode()
+        if rcode != dns.rcode.NOERROR:
+            raise TransferError(rcode)
+        tsig_ctx = r.tsig_ctx
+        first = False
+        answer_index = 0
+        if soa_rrset is None:
+            if not r.answer or r.answer[0].name != oname:
+                raise dns.exception.FormError(
+                    "No answer or RRset not for qname")
+            rrset = r.answer[0]
+            if rrset.rdtype != dns.rdatatype.SOA:
+                raise dns.exception.FormError("first RRset is not an SOA")
+            answer_index = 1
+            soa_rrset = rrset.copy()
+            if rdtype == dns.rdatatype.IXFR:
+                if soa_rrset[0].serial <= serial:
+                    #
+                    # We're already up-to-date.
+                    #
+                    done = True
+                else:
+                    expecting_SOA = True
+        #
+        # Process SOAs in the answer section (other than the initial
+        # SOA in the first message).
+        #
+        for rrset in r.answer[answer_index:]:
+            if done:
+                raise dns.exception.FormError("answers after final SOA")
+            if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname:
+                if expecting_SOA:
+                    if rrset[0].serial != serial:
+                        raise dns.exception.FormError(
+                            "IXFR base serial mismatch")
+                    expecting_SOA = False
+                elif rdtype == dns.rdatatype.IXFR:
+                    delete_mode = not delete_mode
+                #
+                # If this SOA RRset is equal to the first we saw then we're
+                # finished. If this is an IXFR we also check that we're seeing
+                # the record in the expected part of the response.
+                #
+                if rrset == soa_rrset and \
+                        (rdtype == dns.rdatatype.AXFR or
+                         (rdtype == dns.rdatatype.IXFR and delete_mode)):
+                    done = True
+            elif expecting_SOA:
+                #
+                # We made an IXFR request and are expecting another
+                # SOA RR, but saw something else, so this must be an
+                # AXFR response.
+                #
+                rdtype = dns.rdatatype.AXFR
+                expecting_SOA = False
+        if done and q.keyring and not r.had_tsig:
+            raise dns.exception.FormError("missing TSIG")
+        yield r
+    s.close()

+ 15 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/query.pyi

@@ -0,0 +1,15 @@
+from typing import Optional, Union, Dict, Generator, Any
+from . import message, tsig, rdatatype, rdataclass, name, message
+def tcp(q : message.Message, where : str, timeout : float = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port : int = 0,
+        one_rr_per_rrset=False) -> message.Message:
+    pass
+
+def xfr(where : None, zone : Union[name.Name,str], rdtype=rdatatype.AXFR, rdclass=rdataclass.IN,
+        timeout : Optional[float] =None, port=53, keyring : Optional[Dict[name.Name, bytes]] =None, keyname : Union[str,name.Name]=None, relativize=True,
+        af : Optional[int] =None, lifetime : Optional[float]=None, source : Optional[str] =None, source_port=0, serial=0,
+        use_udp=False, keyalgorithm=tsig.default_algorithm) -> Generator[Any,Any,message.Message]:
+    pass
+
+def udp(q : message.Message, where : str, timeout : Optional[float] = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port=0,
+        ignore_unexpected=False, one_rr_per_rrset=False) -> message.Message:
+    ...

+ 144 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rcode.py

@@ -0,0 +1,144 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Result Codes."""
+
+import dns.exception
+from ._compat import long
+
+#: No error
+NOERROR = 0
+#: Form error
+FORMERR = 1
+#: Server failure
+SERVFAIL = 2
+#: Name does not exist ("Name Error" in RFC 1025 terminology).
+NXDOMAIN = 3
+#: Not implemented
+NOTIMP = 4
+#: Refused
+REFUSED = 5
+#: Name exists.
+YXDOMAIN = 6
+#: RRset exists.
+YXRRSET = 7
+#: RRset does not exist.
+NXRRSET = 8
+#: Not authoritative.
+NOTAUTH = 9
+#: Name not in zone.
+NOTZONE = 10
+#: Bad EDNS version.
+BADVERS = 16
+
+_by_text = {
+    'NOERROR': NOERROR,
+    'FORMERR': FORMERR,
+    'SERVFAIL': SERVFAIL,
+    'NXDOMAIN': NXDOMAIN,
+    'NOTIMP': NOTIMP,
+    'REFUSED': REFUSED,
+    'YXDOMAIN': YXDOMAIN,
+    'YXRRSET': YXRRSET,
+    'NXRRSET': NXRRSET,
+    'NOTAUTH': NOTAUTH,
+    'NOTZONE': NOTZONE,
+    'BADVERS': BADVERS
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be a true inverse.
+
+_by_value = {y: x for x, y in _by_text.items()}
+
+
+class UnknownRcode(dns.exception.DNSException):
+    """A DNS rcode is unknown."""
+
+
+def from_text(text):
+    """Convert text into an rcode.
+
+    *text*, a ``text``, the textual rcode or an integer in textual form.
+
+    Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown.
+
+    Returns an ``int``.
+    """
+
+    if text.isdigit():
+        v = int(text)
+        if v >= 0 and v <= 4095:
+            return v
+    v = _by_text.get(text.upper())
+    if v is None:
+        raise UnknownRcode
+    return v
+
+
+def from_flags(flags, ednsflags):
+    """Return the rcode value encoded by flags and ednsflags.
+
+    *flags*, an ``int``, the DNS flags field.
+
+    *ednsflags*, an ``int``, the EDNS flags field.
+
+    Raises ``ValueError`` if rcode is < 0 or > 4095
+
+    Returns an ``int``.
+    """
+
+    value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
+    if value < 0 or value > 4095:
+        raise ValueError('rcode must be >= 0 and <= 4095')
+    return value
+
+
+def to_flags(value):
+    """Return a (flags, ednsflags) tuple which encodes the rcode.
+
+    *value*, an ``int``, the rcode.
+
+    Raises ``ValueError`` if rcode is < 0 or > 4095.
+
+    Returns an ``(int, int)`` tuple.
+    """
+
+    if value < 0 or value > 4095:
+        raise ValueError('rcode must be >= 0 and <= 4095')
+    v = value & 0xf
+    ev = long(value & 0xff0) << 20
+    return (v, ev)
+
+
+def to_text(value):
+    """Convert rcode into text.
+
+    *value*, and ``int``, the rcode.
+
+    Raises ``ValueError`` if rcode is < 0 or > 4095.
+
+    Returns a ``text``.
+    """
+
+    if value < 0 or value > 4095:
+        raise ValueError('rcode must be >= 0 and <= 4095')
+    text = _by_value.get(value)
+    if text is None:
+        text = str(value)
+    return text

+ 456 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdata.py

@@ -0,0 +1,456 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdata."""
+
+from io import BytesIO
+import base64
+import binascii
+
+import dns.exception
+import dns.name
+import dns.rdataclass
+import dns.rdatatype
+import dns.tokenizer
+import dns.wiredata
+from ._compat import xrange, string_types, text_type
+
+try:
+    import threading as _threading
+except ImportError:
+    import dummy_threading as _threading
+
+_hex_chunksize = 32
+
+
+def _hexify(data, chunksize=_hex_chunksize):
+    """Convert a binary string into its hex encoding, broken up into chunks
+    of chunksize characters separated by a space.
+    """
+
+    line = binascii.hexlify(data)
+    return b' '.join([line[i:i + chunksize]
+                      for i
+                      in range(0, len(line), chunksize)]).decode()
+
+_base64_chunksize = 32
+
+
+def _base64ify(data, chunksize=_base64_chunksize):
+    """Convert a binary string into its base64 encoding, broken up into chunks
+    of chunksize characters separated by a space.
+    """
+
+    line = base64.b64encode(data)
+    return b' '.join([line[i:i + chunksize]
+                      for i
+                      in range(0, len(line), chunksize)]).decode()
+
+__escaped = bytearray(b'"\\')
+
+def _escapify(qstring):
+    """Escape the characters in a quoted string which need it."""
+
+    if isinstance(qstring, text_type):
+        qstring = qstring.encode()
+    if not isinstance(qstring, bytearray):
+        qstring = bytearray(qstring)
+
+    text = ''
+    for c in qstring:
+        if c in __escaped:
+            text += '\\' + chr(c)
+        elif c >= 0x20 and c < 0x7F:
+            text += chr(c)
+        else:
+            text += '\\%03d' % c
+    return text
+
+
+def _truncate_bitmap(what):
+    """Determine the index of greatest byte that isn't all zeros, and
+    return the bitmap that contains all the bytes less than that index.
+    """
+
+    for i in xrange(len(what) - 1, -1, -1):
+        if what[i] != 0:
+            return what[0: i + 1]
+    return what[0:1]
+
+
+class Rdata(object):
+    """Base class for all DNS rdata types."""
+
+    __slots__ = ['rdclass', 'rdtype']
+
+    def __init__(self, rdclass, rdtype):
+        """Initialize an rdata.
+
+        *rdclass*, an ``int`` is the rdataclass of the Rdata.
+        *rdtype*, an ``int`` is the rdatatype of the Rdata.
+        """
+
+        self.rdclass = rdclass
+        self.rdtype = rdtype
+
+    def covers(self):
+        """Return the type a Rdata covers.
+
+        DNS SIG/RRSIG rdatas apply to a specific type; this type is
+        returned by the covers() function.  If the rdata type is not
+        SIG or RRSIG, dns.rdatatype.NONE is returned.  This is useful when
+        creating rdatasets, allowing the rdataset to contain only RRSIGs
+        of a particular type, e.g. RRSIG(NS).
+
+        Returns an ``int``.
+        """
+
+        return dns.rdatatype.NONE
+
+    def extended_rdatatype(self):
+        """Return a 32-bit type value, the least significant 16 bits of
+        which are the ordinary DNS type, and the upper 16 bits of which are
+        the "covered" type, if any.
+
+        Returns an ``int``.
+        """
+
+        return self.covers() << 16 | self.rdtype
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        """Convert an rdata to text format.
+
+        Returns a ``text``.
+        """
+
+        raise NotImplementedError
+
+    def to_wire(self, file, compress=None, origin=None):
+        """Convert an rdata to wire format.
+
+        Returns a ``binary``.
+        """
+
+        raise NotImplementedError
+
+    def to_digestable(self, origin=None):
+        """Convert rdata to a format suitable for digesting in hashes.  This
+        is also the DNSSEC canonical form.
+
+        Returns a ``binary``.
+        """
+
+        f = BytesIO()
+        self.to_wire(f, None, origin)
+        return f.getvalue()
+
+    def validate(self):
+        """Check that the current contents of the rdata's fields are
+        valid.
+
+        If you change an rdata by assigning to its fields,
+        it is a good idea to call validate() when you are done making
+        changes.
+
+        Raises various exceptions if there are problems.
+
+        Returns ``None``.
+        """
+
+        dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
+
+    def __repr__(self):
+        covers = self.covers()
+        if covers == dns.rdatatype.NONE:
+            ctext = ''
+        else:
+            ctext = '(' + dns.rdatatype.to_text(covers) + ')'
+        return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
+               dns.rdatatype.to_text(self.rdtype) + ctext + ' rdata: ' + \
+               str(self) + '>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def _cmp(self, other):
+        """Compare an rdata with another rdata of the same rdtype and
+        rdclass.
+
+        Return < 0 if self < other in the DNSSEC ordering, 0 if self
+        == other, and > 0 if self > other.
+
+        """
+        our = self.to_digestable(dns.name.root)
+        their = other.to_digestable(dns.name.root)
+        if our == their:
+            return 0
+        elif our > their:
+            return 1
+        else:
+            return -1
+
+    def __eq__(self, other):
+        if not isinstance(other, Rdata):
+            return False
+        if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+            return False
+        return self._cmp(other) == 0
+
+    def __ne__(self, other):
+        if not isinstance(other, Rdata):
+            return True
+        if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+            return True
+        return self._cmp(other) != 0
+
+    def __lt__(self, other):
+        if not isinstance(other, Rdata) or \
+                self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+
+            return NotImplemented
+        return self._cmp(other) < 0
+
+    def __le__(self, other):
+        if not isinstance(other, Rdata) or \
+                self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) <= 0
+
+    def __ge__(self, other):
+        if not isinstance(other, Rdata) or \
+                self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) >= 0
+
+    def __gt__(self, other):
+        if not isinstance(other, Rdata) or \
+                self.rdclass != other.rdclass or self.rdtype != other.rdtype:
+            return NotImplemented
+        return self._cmp(other) > 0
+
+    def __hash__(self):
+        return hash(self.to_digestable(dns.name.root))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        raise NotImplementedError
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        raise NotImplementedError
+
+    def choose_relativity(self, origin=None, relativize=True):
+        """Convert any domain names in the rdata to the specified
+        relativization.
+        """
+
+class GenericRdata(Rdata):
+
+    """Generic Rdata Class
+
+    This class is used for rdata types for which we have no better
+    implementation.  It implements the DNS "unknown RRs" scheme.
+    """
+
+    __slots__ = ['data']
+
+    def __init__(self, rdclass, rdtype, data):
+        super(GenericRdata, self).__init__(rdclass, rdtype)
+        self.data = data
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return r'\# %d ' % len(self.data) + _hexify(self.data)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        token = tok.get()
+        if not token.is_identifier() or token.value != r'\#':
+            raise dns.exception.SyntaxError(
+                r'generic rdata does not start with \#')
+        length = tok.get_int()
+        chunks = []
+        while 1:
+            token = tok.get()
+            if token.is_eol_or_eof():
+                break
+            chunks.append(token.value.encode())
+        hex = b''.join(chunks)
+        data = binascii.unhexlify(hex)
+        if len(data) != length:
+            raise dns.exception.SyntaxError(
+                'generic rdata hex data has wrong length')
+        return cls(rdclass, rdtype, data)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(self.data)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        return cls(rdclass, rdtype, wire[current: current + rdlen])
+
+_rdata_modules = {}
+_module_prefix = 'dns.rdtypes'
+_import_lock = _threading.Lock()
+
+def get_rdata_class(rdclass, rdtype):
+
+    def import_module(name):
+        with _import_lock:
+            mod = __import__(name)
+            components = name.split('.')
+            for comp in components[1:]:
+                mod = getattr(mod, comp)
+            return mod
+
+    mod = _rdata_modules.get((rdclass, rdtype))
+    rdclass_text = dns.rdataclass.to_text(rdclass)
+    rdtype_text = dns.rdatatype.to_text(rdtype)
+    rdtype_text = rdtype_text.replace('-', '_')
+    if not mod:
+        mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype))
+        if not mod:
+            try:
+                mod = import_module('.'.join([_module_prefix,
+                                              rdclass_text, rdtype_text]))
+                _rdata_modules[(rdclass, rdtype)] = mod
+            except ImportError:
+                try:
+                    mod = import_module('.'.join([_module_prefix,
+                                                  'ANY', rdtype_text]))
+                    _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod
+                except ImportError:
+                    mod = None
+    if mod:
+        cls = getattr(mod, rdtype_text)
+    else:
+        cls = GenericRdata
+    return cls
+
+
+def from_text(rdclass, rdtype, tok, origin=None, relativize=True):
+    """Build an rdata object from text format.
+
+    This function attempts to dynamically load a class which
+    implements the specified rdata class and type.  If there is no
+    class-and-type-specific implementation, the GenericRdata class
+    is used.
+
+    Once a class is chosen, its from_text() class method is called
+    with the parameters to this function.
+
+    If *tok* is a ``text``, then a tokenizer is created and the string
+    is used as its input.
+
+    *rdclass*, an ``int``, the rdataclass.
+
+    *rdtype*, an ``int``, the rdatatype.
+
+    *tok*, a ``dns.tokenizer.Tokenizer`` or a ``text``.
+
+    *origin*, a ``dns.name.Name`` (or ``None``), the
+    origin to use for relative names.
+
+    *relativize*, a ``bool``.  If true, name will be relativized to
+    the specified origin.
+
+    Returns an instance of the chosen Rdata subclass.
+    """
+
+    if isinstance(tok, string_types):
+        tok = dns.tokenizer.Tokenizer(tok)
+    cls = get_rdata_class(rdclass, rdtype)
+    if cls != GenericRdata:
+        # peek at first token
+        token = tok.get()
+        tok.unget(token)
+        if token.is_identifier() and \
+           token.value == r'\#':
+            #
+            # Known type using the generic syntax.  Extract the
+            # wire form from the generic syntax, and then run
+            # from_wire on it.
+            #
+            rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin,
+                                           relativize)
+            return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data),
+                             origin)
+    return cls.from_text(rdclass, rdtype, tok, origin, relativize)
+
+
+def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None):
+    """Build an rdata object from wire format
+
+    This function attempts to dynamically load a class which
+    implements the specified rdata class and type.  If there is no
+    class-and-type-specific implementation, the GenericRdata class
+    is used.
+
+    Once a class is chosen, its from_wire() class method is called
+    with the parameters to this function.
+
+    *rdclass*, an ``int``, the rdataclass.
+
+    *rdtype*, an ``int``, the rdatatype.
+
+    *wire*, a ``binary``, the wire-format message.
+
+    *current*, an ``int``, the offset in wire of the beginning of
+    the rdata.
+
+    *rdlen*, an ``int``, the length of the wire-format rdata
+
+    *origin*, a ``dns.name.Name`` (or ``None``).  If not ``None``,
+    then names will be relativized to this origin.
+
+    Returns an instance of the chosen Rdata subclass.
+    """
+
+    wire = dns.wiredata.maybe_wrap(wire)
+    cls = get_rdata_class(rdclass, rdtype)
+    return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
+
+
+class RdatatypeExists(dns.exception.DNSException):
+    """DNS rdatatype already exists."""
+    supp_kwargs = {'rdclass', 'rdtype'}
+    fmt = "The rdata type with class {rdclass} and rdtype {rdtype} " + \
+        "already exists."
+
+
+def register_type(implementation, rdtype, rdtype_text, is_singleton=False,
+                  rdclass=dns.rdataclass.IN):
+    """Dynamically register a module to handle an rdatatype.
+
+    *implementation*, a module implementing the type in the usual dnspython
+    way.
+
+    *rdtype*, an ``int``, the rdatatype to register.
+
+    *rdtype_text*, a ``text``, the textual form of the rdatatype.
+
+    *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
+    RRsets of the type can have only one member.)
+
+    *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if
+    it applies to all classes.
+    """
+
+    existing_cls = get_rdata_class(rdclass, rdtype)
+    if existing_cls != GenericRdata:
+        raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype)
+    _rdata_modules[(rdclass, rdtype)] = implementation
+    dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton)

+ 17 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdata.pyi

@@ -0,0 +1,17 @@
+from typing import Dict, Tuple, Any, Optional
+from .name import Name
+class Rdata:
+    def __init__(self):
+        self.address : str
+    def to_wire(self, file, compress : Optional[Dict[Name,int]], origin : Optional[Name]) -> bytes:
+        ...
+    @classmethod
+    def from_text(cls, rdclass : int, rdtype : int, tok, origin=None, relativize=True):
+        ...
+_rdata_modules : Dict[Tuple[Any,Rdata],Any]
+
+def from_text(rdclass : int, rdtype : int, tok : Optional[str], origin : Optional[Name] = None, relativize : bool = True):
+    ...
+
+def from_wire(rdclass : int, rdtype : int, wire : bytes, current : int, rdlen : int, origin : Optional[Name] = None):
+    ...

+ 122 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdataclass.py

@@ -0,0 +1,122 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Rdata Classes."""
+
+import re
+
+import dns.exception
+
+RESERVED0 = 0
+IN = 1
+CH = 3
+HS = 4
+NONE = 254
+ANY = 255
+
+_by_text = {
+    'RESERVED0': RESERVED0,
+    'IN': IN,
+    'CH': CH,
+    'HS': HS,
+    'NONE': NONE,
+    'ANY': ANY
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = {y: x for x, y in _by_text.items()}
+
+# Now that we've built the inverse map, we can add class aliases to
+# the _by_text mapping.
+
+_by_text.update({
+    'INTERNET': IN,
+    'CHAOS': CH,
+    'HESIOD': HS
+})
+
+_metaclasses = {
+    NONE: True,
+    ANY: True
+}
+
+_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I)
+
+
+class UnknownRdataclass(dns.exception.DNSException):
+    """A DNS class is unknown."""
+
+
+def from_text(text):
+    """Convert text into a DNS rdata class value.
+
+    The input text can be a defined DNS RR class mnemonic or
+    instance of the DNS generic class syntax.
+
+    For example, "IN" and "CLASS1" will both result in a value of 1.
+
+    Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown.
+
+    Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
+
+    Returns an ``int``.
+    """
+
+    value = _by_text.get(text.upper())
+    if value is None:
+        match = _unknown_class_pattern.match(text)
+        if match is None:
+            raise UnknownRdataclass
+        value = int(match.group(1))
+        if value < 0 or value > 65535:
+            raise ValueError("class must be between >= 0 and <= 65535")
+    return value
+
+
+def to_text(value):
+    """Convert a DNS rdata type value to text.
+
+    If the value has a known mnemonic, it will be used, otherwise the
+    DNS generic class syntax will be used.
+
+    Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
+
+    Returns a ``str``.
+    """
+
+    if value < 0 or value > 65535:
+        raise ValueError("class must be between >= 0 and <= 65535")
+    text = _by_value.get(value)
+    if text is None:
+        text = 'CLASS' + repr(value)
+    return text
+
+
+def is_metaclass(rdclass):
+    """True if the specified class is a metaclass.
+
+    The currently defined metaclasses are ANY and NONE.
+
+    *rdclass* is an ``int``.
+    """
+
+    if rdclass in _metaclasses:
+        return True
+    return False

+ 347 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdataset.py

@@ -0,0 +1,347 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
+
+import random
+from io import StringIO
+import struct
+
+import dns.exception
+import dns.rdatatype
+import dns.rdataclass
+import dns.rdata
+import dns.set
+from ._compat import string_types
+
+# define SimpleSet here for backwards compatibility
+SimpleSet = dns.set.Set
+
+
+class DifferingCovers(dns.exception.DNSException):
+    """An attempt was made to add a DNS SIG/RRSIG whose covered type
+    is not the same as that of the other rdatas in the rdataset."""
+
+
+class IncompatibleTypes(dns.exception.DNSException):
+    """An attempt was made to add DNS RR data of an incompatible type."""
+
+
+class Rdataset(dns.set.Set):
+
+    """A DNS rdataset."""
+
+    __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
+
+    def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0):
+        """Create a new rdataset of the specified class and type.
+
+        *rdclass*, an ``int``, the rdataclass.
+
+        *rdtype*, an ``int``, the rdatatype.
+
+        *covers*, an ``int``, the covered rdatatype.
+
+        *ttl*, an ``int``, the TTL.
+        """
+
+        super(Rdataset, self).__init__()
+        self.rdclass = rdclass
+        self.rdtype = rdtype
+        self.covers = covers
+        self.ttl = ttl
+
+    def _clone(self):
+        obj = super(Rdataset, self)._clone()
+        obj.rdclass = self.rdclass
+        obj.rdtype = self.rdtype
+        obj.covers = self.covers
+        obj.ttl = self.ttl
+        return obj
+
+    def update_ttl(self, ttl):
+        """Perform TTL minimization.
+
+        Set the TTL of the rdataset to be the lesser of the set's current
+        TTL or the specified TTL.  If the set contains no rdatas, set the TTL
+        to the specified TTL.
+
+        *ttl*, an ``int``.
+        """
+
+        if len(self) == 0:
+            self.ttl = ttl
+        elif ttl < self.ttl:
+            self.ttl = ttl
+
+    def add(self, rd, ttl=None):
+        """Add the specified rdata to the rdataset.
+
+        If the optional *ttl* parameter is supplied, then
+        ``self.update_ttl(ttl)`` will be called prior to adding the rdata.
+
+        *rd*, a ``dns.rdata.Rdata``, the rdata
+
+        *ttl*, an ``int``, the TTL.
+
+        Raises ``dns.rdataset.IncompatibleTypes`` if the type and class
+        do not match the type and class of the rdataset.
+
+        Raises ``dns.rdataset.DifferingCovers`` if the type is a signature
+        type and the covered type does not match that of the rdataset.
+        """
+
+        #
+        # If we're adding a signature, do some special handling to
+        # check that the signature covers the same type as the
+        # other rdatas in this rdataset.  If this is the first rdata
+        # in the set, initialize the covers field.
+        #
+        if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype:
+            raise IncompatibleTypes
+        if ttl is not None:
+            self.update_ttl(ttl)
+        if self.rdtype == dns.rdatatype.RRSIG or \
+           self.rdtype == dns.rdatatype.SIG:
+            covers = rd.covers()
+            if len(self) == 0 and self.covers == dns.rdatatype.NONE:
+                self.covers = covers
+            elif self.covers != covers:
+                raise DifferingCovers
+        if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0:
+            self.clear()
+        super(Rdataset, self).add(rd)
+
+    def union_update(self, other):
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).union_update(other)
+
+    def intersection_update(self, other):
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).intersection_update(other)
+
+    def update(self, other):
+        """Add all rdatas in other to self.
+
+        *other*, a ``dns.rdataset.Rdataset``, the rdataset from which
+        to update.
+        """
+
+        self.update_ttl(other.ttl)
+        super(Rdataset, self).update(other)
+
+    def __repr__(self):
+        if self.covers == 0:
+            ctext = ''
+        else:
+            ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
+        return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
+               dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
+
+    def __str__(self):
+        return self.to_text()
+
+    def __eq__(self, other):
+        if not isinstance(other, Rdataset):
+            return False
+        if self.rdclass != other.rdclass or \
+           self.rdtype != other.rdtype or \
+           self.covers != other.covers:
+            return False
+        return super(Rdataset, self).__eq__(other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def to_text(self, name=None, origin=None, relativize=True,
+                override_rdclass=None, **kw):
+        """Convert the rdataset into DNS master file format.
+
+        See ``dns.name.Name.choose_relativity`` for more information
+        on how *origin* and *relativize* determine the way names
+        are emitted.
+
+        Any additional keyword arguments are passed on to the rdata
+        ``to_text()`` method.
+
+        *name*, a ``dns.name.Name``.  If name is not ``None``, emit RRs with
+        *name* as the owner name.
+
+        *origin*, a ``dns.name.Name`` or ``None``, the origin for relative
+        names.
+
+        *relativize*, a ``bool``.  If ``True``, names will be relativized
+        to *origin*.
+        """
+
+        if name is not None:
+            name = name.choose_relativity(origin, relativize)
+            ntext = str(name)
+            pad = ' '
+        else:
+            ntext = ''
+            pad = ''
+        s = StringIO()
+        if override_rdclass is not None:
+            rdclass = override_rdclass
+        else:
+            rdclass = self.rdclass
+        if len(self) == 0:
+            #
+            # Empty rdatasets are used for the question section, and in
+            # some dynamic updates, so we don't need to print out the TTL
+            # (which is meaningless anyway).
+            #
+            s.write(u'{}{}{} {}\n'.format(ntext, pad,
+                                          dns.rdataclass.to_text(rdclass),
+                                          dns.rdatatype.to_text(self.rdtype)))
+        else:
+            for rd in self:
+                s.write(u'%s%s%d %s %s %s\n' %
+                        (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass),
+                         dns.rdatatype.to_text(self.rdtype),
+                         rd.to_text(origin=origin, relativize=relativize,
+                         **kw)))
+        #
+        # We strip off the final \n for the caller's convenience in printing
+        #
+        return s.getvalue()[:-1]
+
+    def to_wire(self, name, file, compress=None, origin=None,
+                override_rdclass=None, want_shuffle=True):
+        """Convert the rdataset to wire format.
+
+        *name*, a ``dns.name.Name`` is the owner name to use.
+
+        *file* is the file where the name is emitted (typically a
+        BytesIO file).
+
+        *compress*, a ``dict``, is the compression table to use.  If
+        ``None`` (the default), names will not be compressed.
+
+        *origin* is a ``dns.name.Name`` or ``None``.  If the name is
+        relative and origin is not ``None``, then *origin* will be appended
+        to it.
+
+        *override_rdclass*, an ``int``, is used as the class instead of the
+        class of the rdataset.  This is useful when rendering rdatasets
+        associated with dynamic updates.
+
+        *want_shuffle*, a ``bool``.  If ``True``, then the order of the
+        Rdatas within the Rdataset will be shuffled before rendering.
+
+        Returns an ``int``, the number of records emitted.
+        """
+
+        if override_rdclass is not None:
+            rdclass = override_rdclass
+            want_shuffle = False
+        else:
+            rdclass = self.rdclass
+        file.seek(0, 2)
+        if len(self) == 0:
+            name.to_wire(file, compress, origin)
+            stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)
+            file.write(stuff)
+            return 1
+        else:
+            if want_shuffle:
+                l = list(self)
+                random.shuffle(l)
+            else:
+                l = self
+            for rd in l:
+                name.to_wire(file, compress, origin)
+                stuff = struct.pack("!HHIH", self.rdtype, rdclass,
+                                    self.ttl, 0)
+                file.write(stuff)
+                start = file.tell()
+                rd.to_wire(file, compress, origin)
+                end = file.tell()
+                assert end - start < 65536
+                file.seek(start - 2)
+                stuff = struct.pack("!H", end - start)
+                file.write(stuff)
+                file.seek(0, 2)
+            return len(self)
+
+    def match(self, rdclass, rdtype, covers):
+        """Returns ``True`` if this rdataset matches the specified class,
+        type, and covers.
+        """
+        if self.rdclass == rdclass and \
+           self.rdtype == rdtype and \
+           self.covers == covers:
+            return True
+        return False
+
+
+def from_text_list(rdclass, rdtype, ttl, text_rdatas):
+    """Create an rdataset with the specified class, type, and TTL, and with
+    the specified list of rdatas in text format.
+
+    Returns a ``dns.rdataset.Rdataset`` object.
+    """
+
+    if isinstance(rdclass, string_types):
+        rdclass = dns.rdataclass.from_text(rdclass)
+    if isinstance(rdtype, string_types):
+        rdtype = dns.rdatatype.from_text(rdtype)
+    r = Rdataset(rdclass, rdtype)
+    r.update_ttl(ttl)
+    for t in text_rdatas:
+        rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
+        r.add(rd)
+    return r
+
+
+def from_text(rdclass, rdtype, ttl, *text_rdatas):
+    """Create an rdataset with the specified class, type, and TTL, and with
+    the specified rdatas in text format.
+
+    Returns a ``dns.rdataset.Rdataset`` object.
+    """
+
+    return from_text_list(rdclass, rdtype, ttl, text_rdatas)
+
+
+def from_rdata_list(ttl, rdatas):
+    """Create an rdataset with the specified TTL, and with
+    the specified list of rdata objects.
+
+    Returns a ``dns.rdataset.Rdataset`` object.
+    """
+
+    if len(rdatas) == 0:
+        raise ValueError("rdata list must not be empty")
+    r = None
+    for rd in rdatas:
+        if r is None:
+            r = Rdataset(rd.rdclass, rd.rdtype)
+            r.update_ttl(ttl)
+        r.add(rd)
+    return r
+
+
+def from_rdata(ttl, *rdatas):
+    """Create an rdataset with the specified TTL, and with
+    the specified rdata objects.
+
+    Returns a ``dns.rdataset.Rdataset`` object.
+    """
+
+    return from_rdata_list(ttl, rdatas)

+ 58 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdataset.pyi

@@ -0,0 +1,58 @@
+from typing import Optional, Dict, List, Union
+from io import BytesIO
+from . import exception, name, set, rdatatype, rdata, rdataset
+
+class DifferingCovers(exception.DNSException):
+    """An attempt was made to add a DNS SIG/RRSIG whose covered type
+    is not the same as that of the other rdatas in the rdataset."""
+
+
+class IncompatibleTypes(exception.DNSException):
+    """An attempt was made to add DNS RR data of an incompatible type."""
+
+
+class Rdataset(set.Set):
+    def __init__(self, rdclass, rdtype, covers=rdatatype.NONE, ttl=0):
+        self.rdclass : int = rdclass
+        self.rdtype : int = rdtype
+        self.covers : int = covers
+        self.ttl : int = ttl
+
+    def update_ttl(self, ttl : int) -> None:
+        ...
+
+    def add(self, rd : rdata.Rdata, ttl : Optional[int] =None):
+        ...
+
+    def union_update(self, other : Rdataset):
+        ...
+
+    def intersection_update(self, other : Rdataset):
+        ...
+
+    def update(self, other : Rdataset):
+        ...
+
+    def to_text(self, name : Optional[name.Name] =None, origin : Optional[name.Name] =None, relativize=True,
+                override_rdclass : Optional[int] =None, **kw) -> bytes:
+        ...
+
+    def to_wire(self, name : Optional[name.Name], file : BytesIO, compress : Optional[Dict[name.Name, int]] = None, origin : Optional[name.Name] = None,
+                override_rdclass : Optional[int] = None, want_shuffle=True) -> int:
+        ...
+
+    def match(self, rdclass : int, rdtype : int, covers : int) -> bool:
+        ...
+
+
+def from_text_list(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, text_rdatas : str) -> rdataset.Rdataset:
+    ...
+
+def from_text(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, *text_rdatas : str) -> rdataset.Rdataset:
+    ...
+
+def from_rdata_list(ttl : int, rdatas : List[rdata.Rdata]) -> rdataset.Rdataset:
+    ...
+
+def from_rdata(ttl : int, *rdatas : List[rdata.Rdata]) -> rdataset.Rdataset:
+    ...

+ 287 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdatatype.py

@@ -0,0 +1,287 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS Rdata Types."""
+
+import re
+
+import dns.exception
+
+NONE = 0
+A = 1
+NS = 2
+MD = 3
+MF = 4
+CNAME = 5
+SOA = 6
+MB = 7
+MG = 8
+MR = 9
+NULL = 10
+WKS = 11
+PTR = 12
+HINFO = 13
+MINFO = 14
+MX = 15
+TXT = 16
+RP = 17
+AFSDB = 18
+X25 = 19
+ISDN = 20
+RT = 21
+NSAP = 22
+NSAP_PTR = 23
+SIG = 24
+KEY = 25
+PX = 26
+GPOS = 27
+AAAA = 28
+LOC = 29
+NXT = 30
+SRV = 33
+NAPTR = 35
+KX = 36
+CERT = 37
+A6 = 38
+DNAME = 39
+OPT = 41
+APL = 42
+DS = 43
+SSHFP = 44
+IPSECKEY = 45
+RRSIG = 46
+NSEC = 47
+DNSKEY = 48
+DHCID = 49
+NSEC3 = 50
+NSEC3PARAM = 51
+TLSA = 52
+HIP = 55
+CDS = 59
+CDNSKEY = 60
+OPENPGPKEY = 61
+CSYNC = 62
+SPF = 99
+UNSPEC = 103
+EUI48 = 108
+EUI64 = 109
+TKEY = 249
+TSIG = 250
+IXFR = 251
+AXFR = 252
+MAILB = 253
+MAILA = 254
+ANY = 255
+URI = 256
+CAA = 257
+AVC = 258
+TA = 32768
+DLV = 32769
+
+_by_text = {
+    'NONE': NONE,
+    'A': A,
+    'NS': NS,
+    'MD': MD,
+    'MF': MF,
+    'CNAME': CNAME,
+    'SOA': SOA,
+    'MB': MB,
+    'MG': MG,
+    'MR': MR,
+    'NULL': NULL,
+    'WKS': WKS,
+    'PTR': PTR,
+    'HINFO': HINFO,
+    'MINFO': MINFO,
+    'MX': MX,
+    'TXT': TXT,
+    'RP': RP,
+    'AFSDB': AFSDB,
+    'X25': X25,
+    'ISDN': ISDN,
+    'RT': RT,
+    'NSAP': NSAP,
+    'NSAP-PTR': NSAP_PTR,
+    'SIG': SIG,
+    'KEY': KEY,
+    'PX': PX,
+    'GPOS': GPOS,
+    'AAAA': AAAA,
+    'LOC': LOC,
+    'NXT': NXT,
+    'SRV': SRV,
+    'NAPTR': NAPTR,
+    'KX': KX,
+    'CERT': CERT,
+    'A6': A6,
+    'DNAME': DNAME,
+    'OPT': OPT,
+    'APL': APL,
+    'DS': DS,
+    'SSHFP': SSHFP,
+    'IPSECKEY': IPSECKEY,
+    'RRSIG': RRSIG,
+    'NSEC': NSEC,
+    'DNSKEY': DNSKEY,
+    'DHCID': DHCID,
+    'NSEC3': NSEC3,
+    'NSEC3PARAM': NSEC3PARAM,
+    'TLSA': TLSA,
+    'HIP': HIP,
+    'CDS': CDS,
+    'CDNSKEY': CDNSKEY,
+    'OPENPGPKEY': OPENPGPKEY,
+    'CSYNC': CSYNC,
+    'SPF': SPF,
+    'UNSPEC': UNSPEC,
+    'EUI48': EUI48,
+    'EUI64': EUI64,
+    'TKEY': TKEY,
+    'TSIG': TSIG,
+    'IXFR': IXFR,
+    'AXFR': AXFR,
+    'MAILB': MAILB,
+    'MAILA': MAILA,
+    'ANY': ANY,
+    'URI': URI,
+    'CAA': CAA,
+    'AVC': AVC,
+    'TA': TA,
+    'DLV': DLV,
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+
+_by_value = {y: x for x, y in _by_text.items()}
+
+_metatypes = {
+    OPT: True
+}
+
+_singletons = {
+    SOA: True,
+    NXT: True,
+    DNAME: True,
+    NSEC: True,
+    CNAME: True,
+}
+
+_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I)
+
+
+class UnknownRdatatype(dns.exception.DNSException):
+    """DNS resource record type is unknown."""
+
+
+def from_text(text):
+    """Convert text into a DNS rdata type value.
+
+    The input text can be a defined DNS RR type mnemonic or
+    instance of the DNS generic type syntax.
+
+    For example, "NS" and "TYPE2" will both result in a value of 2.
+
+    Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown.
+
+    Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
+
+    Returns an ``int``.
+    """
+
+    value = _by_text.get(text.upper())
+    if value is None:
+        match = _unknown_type_pattern.match(text)
+        if match is None:
+            raise UnknownRdatatype
+        value = int(match.group(1))
+        if value < 0 or value > 65535:
+            raise ValueError("type must be between >= 0 and <= 65535")
+    return value
+
+
+def to_text(value):
+    """Convert a DNS rdata type value to text.
+
+    If the value has a known mnemonic, it will be used, otherwise the
+    DNS generic type syntax will be used.
+
+    Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
+
+    Returns a ``str``.
+    """
+
+    if value < 0 or value > 65535:
+        raise ValueError("type must be between >= 0 and <= 65535")
+    text = _by_value.get(value)
+    if text is None:
+        text = 'TYPE' + repr(value)
+    return text
+
+
+def is_metatype(rdtype):
+    """True if the specified type is a metatype.
+
+    *rdtype* is an ``int``.
+
+    The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA,
+    MAILB, ANY, and OPT.
+
+    Returns a ``bool``.
+    """
+
+    if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes:
+        return True
+    return False
+
+
+def is_singleton(rdtype):
+    """Is the specified type a singleton type?
+
+    Singleton types can only have a single rdata in an rdataset, or a single
+    RR in an RRset.
+
+    The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and
+    SOA.
+
+    *rdtype* is an ``int``.
+
+    Returns a ``bool``.
+    """
+
+    if rdtype in _singletons:
+        return True
+    return False
+
+
+def register_type(rdtype, rdtype_text, is_singleton=False):  # pylint: disable=redefined-outer-name
+    """Dynamically register an rdatatype.
+
+    *rdtype*, an ``int``, the rdatatype to register.
+
+    *rdtype_text*, a ``text``, the textual form of the rdatatype.
+
+    *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
+    RRsets of the type can have only one member.)
+    """
+
+    _by_text[rdtype_text] = rdtype
+    _by_value[rdtype] = rdtype_text
+    if is_singleton:
+        _singletons[rdtype] = True

+ 55 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/AFSDB.py

@@ -0,0 +1,55 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+
+class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+
+    """AFSDB record
+
+    @ivar subtype: the subtype value
+    @type subtype: int
+    @ivar hostname: the hostname name
+    @type hostname: dns.name.Name object"""
+
+    # Use the property mechanism to make "subtype" an alias for the
+    # "preference" attribute, and "hostname" an alias for the "exchange"
+    # attribute.
+    #
+    # This lets us inherit the UncompressedMX implementation but lets
+    # the caller use appropriate attribute names for the rdata type.
+    #
+    # We probably lose some performance vs. a cut-and-paste
+    # implementation, but this way we don't copy code, and that's
+    # good.
+
+    def get_subtype(self):
+        return self.preference
+
+    def set_subtype(self, subtype):
+        self.preference = subtype
+
+    subtype = property(get_subtype, set_subtype)
+
+    def get_hostname(self):
+        return self.exchange
+
+    def set_hostname(self, hostname):
+        self.exchange = hostname
+
+    hostname = property(get_hostname, set_hostname)

+ 25 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/AVC.py

@@ -0,0 +1,25 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2016 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.txtbase
+
+
+class AVC(dns.rdtypes.txtbase.TXTBase):
+
+    """AVC record
+
+    @see: U{http://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template}"""

+ 75 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CAA.py

@@ -0,0 +1,75 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+
+class CAA(dns.rdata.Rdata):
+
+    """CAA (Certification Authority Authorization) record
+
+    @ivar flags: the flags
+    @type flags: int
+    @ivar tag: the tag
+    @type tag: string
+    @ivar value: the value
+    @type value: string
+    @see: RFC 6844"""
+
+    __slots__ = ['flags', 'tag', 'value']
+
+    def __init__(self, rdclass, rdtype, flags, tag, value):
+        super(CAA, self).__init__(rdclass, rdtype)
+        self.flags = flags
+        self.tag = tag
+        self.value = value
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%u %s "%s"' % (self.flags,
+                               dns.rdata._escapify(self.tag),
+                               dns.rdata._escapify(self.value))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        flags = tok.get_uint8()
+        tag = tok.get_string().encode()
+        if len(tag) > 255:
+            raise dns.exception.SyntaxError("tag too long")
+        if not tag.isalnum():
+            raise dns.exception.SyntaxError("tag is not alphanumeric")
+        value = tok.get_string().encode()
+        return cls(rdclass, rdtype, flags, tag, value)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(struct.pack('!B', self.flags))
+        l = len(self.tag)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.tag)
+        file.write(self.value)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (flags, l) = struct.unpack('!BB', wire[current: current + 2])
+        current += 2
+        tag = wire[current: current + l]
+        value = wire[current + l:current + rdlen - 2]
+        return cls(rdclass, rdtype, flags, tag, value)

+ 27 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CDNSKEY.py

@@ -0,0 +1,27 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dnskeybase
+from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set
+
+
+__all__ = ['flags_to_text_set', 'flags_from_text_set']
+
+
+class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
+
+    """CDNSKEY record"""

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CDS.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dsbase
+
+
+class CDS(dns.rdtypes.dsbase.DSBase):
+
+    """CDS record"""

+ 123 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CERT.py

@@ -0,0 +1,123 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import base64
+
+import dns.exception
+import dns.dnssec
+import dns.rdata
+import dns.tokenizer
+
+_ctype_by_value = {
+    1: 'PKIX',
+    2: 'SPKI',
+    3: 'PGP',
+    253: 'URI',
+    254: 'OID',
+}
+
+_ctype_by_name = {
+    'PKIX': 1,
+    'SPKI': 2,
+    'PGP': 3,
+    'URI': 253,
+    'OID': 254,
+}
+
+
+def _ctype_from_text(what):
+    v = _ctype_by_name.get(what)
+    if v is not None:
+        return v
+    return int(what)
+
+
+def _ctype_to_text(what):
+    v = _ctype_by_value.get(what)
+    if v is not None:
+        return v
+    return str(what)
+
+
+class CERT(dns.rdata.Rdata):
+
+    """CERT record
+
+    @ivar certificate_type: certificate type
+    @type certificate_type: int
+    @ivar key_tag: key tag
+    @type key_tag: int
+    @ivar algorithm: algorithm
+    @type algorithm: int
+    @ivar certificate: the certificate or CRL
+    @type certificate: string
+    @see: RFC 2538"""
+
+    __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate']
+
+    def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm,
+                 certificate):
+        super(CERT, self).__init__(rdclass, rdtype)
+        self.certificate_type = certificate_type
+        self.key_tag = key_tag
+        self.algorithm = algorithm
+        self.certificate = certificate
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        certificate_type = _ctype_to_text(self.certificate_type)
+        return "%s %d %s %s" % (certificate_type, self.key_tag,
+                                dns.dnssec.algorithm_to_text(self.algorithm),
+                                dns.rdata._base64ify(self.certificate))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        certificate_type = _ctype_from_text(tok.get_string())
+        key_tag = tok.get_uint16()
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        if algorithm < 0 or algorithm > 255:
+            raise dns.exception.SyntaxError("bad algorithm type")
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        certificate = base64.b64decode(b64)
+        return cls(rdclass, rdtype, certificate_type, key_tag,
+                   algorithm, certificate)
+
+    def to_wire(self, file, compress=None, origin=None):
+        prefix = struct.pack("!HHB", self.certificate_type, self.key_tag,
+                             self.algorithm)
+        file.write(prefix)
+        file.write(self.certificate)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        prefix = wire[current: current + 5].unwrap()
+        current += 5
+        rdlen -= 5
+        if rdlen < 0:
+            raise dns.exception.FormError
+        (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix)
+        certificate = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, certificate_type, key_tag, algorithm,
+                   certificate)

+ 27 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CNAME.py

@@ -0,0 +1,27 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+
+class CNAME(dns.rdtypes.nsbase.NSBase):
+
+    """CNAME record
+
+    Note: although CNAME is officially a singleton type, dnspython allows
+    non-singleton CNAME rdatasets because such sets have been commonly
+    used by BIND and other nameservers for load balancing."""

+ 126 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/CSYNC.py

@@ -0,0 +1,126 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+import dns.name
+from dns._compat import xrange
+
+class CSYNC(dns.rdata.Rdata):
+
+    """CSYNC record
+
+    @ivar serial: the SOA serial number
+    @type serial: int
+    @ivar flags: the CSYNC flags
+    @type flags: int
+    @ivar windows: the windowed bitmap list
+    @type windows: list of (window number, string) tuples"""
+
+    __slots__ = ['serial', 'flags', 'windows']
+
+    def __init__(self, rdclass, rdtype, serial, flags, windows):
+        super(CSYNC, self).__init__(rdclass, rdtype)
+        self.serial = serial
+        self.flags = flags
+        self.windows = windows
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        text = ''
+        for (window, bitmap) in self.windows:
+            bits = []
+            for i in xrange(0, len(bitmap)):
+                byte = bitmap[i]
+                for j in xrange(0, 8):
+                    if byte & (0x80 >> j):
+                        bits.append(dns.rdatatype.to_text(window * 256 +
+                                                          i * 8 + j))
+            text += (' ' + ' '.join(bits))
+        return '%d %d%s' % (self.serial, self.flags, text)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        serial = tok.get_uint32()
+        flags = tok.get_uint16()
+        rdtypes = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("CSYNC with bit 0")
+            if nrdtype > 65535:
+                raise dns.exception.SyntaxError("CSYNC with bit > 65535")
+            rdtypes.append(nrdtype)
+        rdtypes.sort()
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = bytearray(b'\0' * 32)
+        windows = []
+        for nrdtype in rdtypes:
+            if nrdtype == prior_rdtype:
+                continue
+            prior_rdtype = nrdtype
+            new_window = nrdtype // 256
+            if new_window != window:
+                windows.append((window, bitmap[0:octets]))
+                bitmap = bytearray(b'\0' * 32)
+                window = new_window
+            offset = nrdtype % 256
+            byte = offset // 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = bitmap[byte] | (0x80 >> bit)
+
+        windows.append((window, bitmap[0:octets]))
+        return cls(rdclass, rdtype, serial, flags, windows)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(struct.pack('!IH', self.serial, self.flags))
+        for (window, bitmap) in self.windows:
+            file.write(struct.pack('!BB', window, len(bitmap)))
+            file.write(bitmap)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        if rdlen < 6:
+            raise dns.exception.FormError("CSYNC too short")
+        (serial, flags) = struct.unpack("!IH", wire[current: current + 6])
+        current += 6
+        rdlen -= 6
+        windows = []
+        while rdlen > 0:
+            if rdlen < 3:
+                raise dns.exception.FormError("CSYNC too short")
+            window = wire[current]
+            octets = wire[current + 1]
+            if octets == 0 or octets > 32:
+                raise dns.exception.FormError("bad CSYNC octets")
+            current += 2
+            rdlen -= 2
+            if rdlen < octets:
+                raise dns.exception.FormError("bad CSYNC bitmap length")
+            bitmap = bytearray(wire[current: current + octets].unwrap())
+            current += octets
+            rdlen -= octets
+            windows.append((window, bitmap))
+        return cls(rdclass, rdtype, serial, flags, windows)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DLV.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dsbase
+
+
+class DLV(dns.rdtypes.dsbase.DSBase):
+
+    """DLV record"""

+ 26 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DNAME.py

@@ -0,0 +1,26 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+
+class DNAME(dns.rdtypes.nsbase.UncompressedNS):
+
+    """DNAME record"""
+
+    def to_digestable(self, origin=None):
+        return self.target.to_digestable(origin)

+ 27 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DNSKEY.py

@@ -0,0 +1,27 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dnskeybase
+from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set
+
+
+__all__ = ['flags_to_text_set', 'flags_from_text_set']
+
+
+class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
+
+    """DNSKEY record"""

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/DS.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.dsbase
+
+
+class DS(dns.rdtypes.dsbase.DSBase):
+
+    """DS record"""

+ 29 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/EUI48.py

@@ -0,0 +1,29 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.euibase
+
+
+class EUI48(dns.rdtypes.euibase.EUIBase):
+
+    """EUI48 record
+
+    @ivar fingerprint: 48-bit Extended Unique Identifier (EUI-48)
+    @type fingerprint: string
+    @see: rfc7043.txt"""
+
+    byte_len = 6  # 0123456789ab (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab

+ 29 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/EUI64.py

@@ -0,0 +1,29 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.euibase
+
+
+class EUI64(dns.rdtypes.euibase.EUIBase):
+
+    """EUI64 record
+
+    @ivar fingerprint: 64-bit Extended Unique Identifier (EUI-64)
+    @type fingerprint: string
+    @see: rfc7043.txt"""
+
+    byte_len = 8  # 0123456789abcdef (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab-cd-ef

+ 162 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/GPOS.py

@@ -0,0 +1,162 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+from dns._compat import long, text_type
+
+
+def _validate_float_string(what):
+    if what[0] == b'-'[0] or what[0] == b'+'[0]:
+        what = what[1:]
+    if what.isdigit():
+        return
+    (left, right) = what.split(b'.')
+    if left == b'' and right == b'':
+        raise dns.exception.FormError
+    if not left == b'' and not left.decode().isdigit():
+        raise dns.exception.FormError
+    if not right == b'' and not right.decode().isdigit():
+        raise dns.exception.FormError
+
+
+def _sanitize(value):
+    if isinstance(value, text_type):
+        return value.encode()
+    return value
+
+
+class GPOS(dns.rdata.Rdata):
+
+    """GPOS record
+
+    @ivar latitude: latitude
+    @type latitude: string
+    @ivar longitude: longitude
+    @type longitude: string
+    @ivar altitude: altitude
+    @type altitude: string
+    @see: RFC 1712"""
+
+    __slots__ = ['latitude', 'longitude', 'altitude']
+
+    def __init__(self, rdclass, rdtype, latitude, longitude, altitude):
+        super(GPOS, self).__init__(rdclass, rdtype)
+        if isinstance(latitude, float) or \
+           isinstance(latitude, int) or \
+           isinstance(latitude, long):
+            latitude = str(latitude)
+        if isinstance(longitude, float) or \
+           isinstance(longitude, int) or \
+           isinstance(longitude, long):
+            longitude = str(longitude)
+        if isinstance(altitude, float) or \
+           isinstance(altitude, int) or \
+           isinstance(altitude, long):
+            altitude = str(altitude)
+        latitude = _sanitize(latitude)
+        longitude = _sanitize(longitude)
+        altitude = _sanitize(altitude)
+        _validate_float_string(latitude)
+        _validate_float_string(longitude)
+        _validate_float_string(altitude)
+        self.latitude = latitude
+        self.longitude = longitude
+        self.altitude = altitude
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '{} {} {}'.format(self.latitude.decode(),
+                             self.longitude.decode(),
+                             self.altitude.decode())
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        latitude = tok.get_string()
+        longitude = tok.get_string()
+        altitude = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.latitude)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.latitude)
+        l = len(self.longitude)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.longitude)
+        l = len(self.altitude)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.altitude)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        latitude = wire[current: current + l].unwrap()
+        current += l
+        rdlen -= l
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        longitude = wire[current: current + l].unwrap()
+        current += l
+        rdlen -= l
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        altitude = wire[current: current + l].unwrap()
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+
+    def _get_float_latitude(self):
+        return float(self.latitude)
+
+    def _set_float_latitude(self, value):
+        self.latitude = str(value)
+
+    float_latitude = property(_get_float_latitude, _set_float_latitude,
+                              doc="latitude as a floating point value")
+
+    def _get_float_longitude(self):
+        return float(self.longitude)
+
+    def _set_float_longitude(self, value):
+        self.longitude = str(value)
+
+    float_longitude = property(_get_float_longitude, _set_float_longitude,
+                               doc="longitude as a floating point value")
+
+    def _get_float_altitude(self):
+        return float(self.altitude)
+
+    def _set_float_altitude(self, value):
+        self.altitude = str(value)
+
+    float_altitude = property(_get_float_altitude, _set_float_altitude,
+                              doc="altitude as a floating point value")

+ 86 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/HINFO.py

@@ -0,0 +1,86 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+from dns._compat import text_type
+
+
+class HINFO(dns.rdata.Rdata):
+
+    """HINFO record
+
+    @ivar cpu: the CPU type
+    @type cpu: string
+    @ivar os: the OS type
+    @type os: string
+    @see: RFC 1035"""
+
+    __slots__ = ['cpu', 'os']
+
+    def __init__(self, rdclass, rdtype, cpu, os):
+        super(HINFO, self).__init__(rdclass, rdtype)
+        if isinstance(cpu, text_type):
+            self.cpu = cpu.encode()
+        else:
+            self.cpu = cpu
+        if isinstance(os, text_type):
+            self.os = os.encode()
+        else:
+            self.os = os
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '"{}" "{}"'.format(dns.rdata._escapify(self.cpu),
+                                  dns.rdata._escapify(self.os))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        cpu = tok.get_string()
+        os = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, cpu, os)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.cpu)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.cpu)
+        l = len(self.os)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.os)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        cpu = wire[current:current + l].unwrap()
+        current += l
+        rdlen -= l
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        os = wire[current: current + l].unwrap()
+        return cls(rdclass, rdtype, cpu, os)

+ 115 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/HIP.py

@@ -0,0 +1,115 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2010, 2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import base64
+import binascii
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+
+
+class HIP(dns.rdata.Rdata):
+
+    """HIP record
+
+    @ivar hit: the host identity tag
+    @type hit: string
+    @ivar algorithm: the public key cryptographic algorithm
+    @type algorithm: int
+    @ivar key: the public key
+    @type key: string
+    @ivar servers: the rendezvous servers
+    @type servers: list of dns.name.Name objects
+    @see: RFC 5205"""
+
+    __slots__ = ['hit', 'algorithm', 'key', 'servers']
+
+    def __init__(self, rdclass, rdtype, hit, algorithm, key, servers):
+        super(HIP, self).__init__(rdclass, rdtype)
+        self.hit = hit
+        self.algorithm = algorithm
+        self.key = key
+        self.servers = servers
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        hit = binascii.hexlify(self.hit).decode()
+        key = base64.b64encode(self.key).replace(b'\n', b'').decode()
+        text = u''
+        servers = []
+        for server in self.servers:
+            servers.append(server.choose_relativity(origin, relativize))
+        if len(servers) > 0:
+            text += (u' ' + u' '.join((x.to_unicode() for x in servers)))
+        return u'%u %s %s%s' % (self.algorithm, hit, key, text)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        algorithm = tok.get_uint8()
+        hit = binascii.unhexlify(tok.get_string().encode())
+        if len(hit) > 255:
+            raise dns.exception.SyntaxError("HIT too long")
+        key = base64.b64decode(tok.get_string().encode())
+        servers = []
+        while 1:
+            token = tok.get()
+            if token.is_eol_or_eof():
+                break
+            server = dns.name.from_text(token.value, origin)
+            server.choose_relativity(origin, relativize)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
+
+    def to_wire(self, file, compress=None, origin=None):
+        lh = len(self.hit)
+        lk = len(self.key)
+        file.write(struct.pack("!BBH", lh, self.algorithm, lk))
+        file.write(self.hit)
+        file.write(self.key)
+        for server in self.servers:
+            server.to_wire(file, None, origin)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (lh, algorithm, lk) = struct.unpack('!BBH',
+                                            wire[current: current + 4])
+        current += 4
+        rdlen -= 4
+        hit = wire[current: current + lh].unwrap()
+        current += lh
+        rdlen -= lh
+        key = wire[current: current + lk].unwrap()
+        current += lk
+        rdlen -= lk
+        servers = []
+        while rdlen > 0:
+            (server, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                 current)
+            current += cused
+            rdlen -= cused
+            if origin is not None:
+                server = server.relativize(origin)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        servers = []
+        for server in self.servers:
+            server = server.choose_relativity(origin, relativize)
+            servers.append(server)
+        self.servers = servers

+ 99 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/ISDN.py

@@ -0,0 +1,99 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+from dns._compat import text_type
+
+
+class ISDN(dns.rdata.Rdata):
+
+    """ISDN record
+
+    @ivar address: the ISDN address
+    @type address: string
+    @ivar subaddress: the ISDN subaddress (or '' if not present)
+    @type subaddress: string
+    @see: RFC 1183"""
+
+    __slots__ = ['address', 'subaddress']
+
+    def __init__(self, rdclass, rdtype, address, subaddress):
+        super(ISDN, self).__init__(rdclass, rdtype)
+        if isinstance(address, text_type):
+            self.address = address.encode()
+        else:
+            self.address = address
+        if isinstance(address, text_type):
+            self.subaddress = subaddress.encode()
+        else:
+            self.subaddress = subaddress
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.subaddress:
+            return '"{}" "{}"'.format(dns.rdata._escapify(self.address),
+                                  dns.rdata._escapify(self.subaddress))
+        else:
+            return '"%s"' % dns.rdata._escapify(self.address)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_string()
+        t = tok.get()
+        if not t.is_eol_or_eof():
+            tok.unget(t)
+            subaddress = tok.get_string()
+        else:
+            tok.unget(t)
+            subaddress = ''
+        tok.get_eol()
+        return cls(rdclass, rdtype, address, subaddress)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.address)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.address)
+        l = len(self.subaddress)
+        if l > 0:
+            assert l < 256
+            file.write(struct.pack('!B', l))
+            file.write(self.subaddress)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l > rdlen:
+            raise dns.exception.FormError
+        address = wire[current: current + l].unwrap()
+        current += l
+        rdlen -= l
+        if rdlen > 0:
+            l = wire[current]
+            current += 1
+            rdlen -= 1
+            if l != rdlen:
+                raise dns.exception.FormError
+            subaddress = wire[current: current + l].unwrap()
+        else:
+            subaddress = ''
+        return cls(rdclass, rdtype, address, subaddress)

+ 327 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/LOC.py

@@ -0,0 +1,327 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from __future__ import division
+
+import struct
+
+import dns.exception
+import dns.rdata
+from dns._compat import long, xrange, round_py2_compat
+
+
+_pows = tuple(long(10**i) for i in range(0, 11))
+
+# default values are in centimeters
+_default_size = 100.0
+_default_hprec = 1000000.0
+_default_vprec = 1000.0
+
+
+def _exponent_of(what, desc):
+    if what == 0:
+        return 0
+    exp = None
+    for i in xrange(len(_pows)):
+        if what // _pows[i] == long(0):
+            exp = i - 1
+            break
+    if exp is None or exp < 0:
+        raise dns.exception.SyntaxError("%s value out of bounds" % desc)
+    return exp
+
+
+def _float_to_tuple(what):
+    if what < 0:
+        sign = -1
+        what *= -1
+    else:
+        sign = 1
+    what = round_py2_compat(what * 3600000)
+    degrees = int(what // 3600000)
+    what -= degrees * 3600000
+    minutes = int(what // 60000)
+    what -= minutes * 60000
+    seconds = int(what // 1000)
+    what -= int(seconds * 1000)
+    what = int(what)
+    return (degrees, minutes, seconds, what, sign)
+
+
+def _tuple_to_float(what):
+    value = float(what[0])
+    value += float(what[1]) / 60.0
+    value += float(what[2]) / 3600.0
+    value += float(what[3]) / 3600000.0
+    return float(what[4]) * value
+
+
+def _encode_size(what, desc):
+    what = long(what)
+    exponent = _exponent_of(what, desc) & 0xF
+    base = what // pow(10, exponent) & 0xF
+    return base * 16 + exponent
+
+
+def _decode_size(what, desc):
+    exponent = what & 0x0F
+    if exponent > 9:
+        raise dns.exception.SyntaxError("bad %s exponent" % desc)
+    base = (what & 0xF0) >> 4
+    if base > 9:
+        raise dns.exception.SyntaxError("bad %s base" % desc)
+    return long(base) * pow(10, exponent)
+
+
+class LOC(dns.rdata.Rdata):
+
+    """LOC record
+
+    @ivar latitude: latitude
+    @type latitude: (int, int, int, int, sign) tuple specifying the degrees, minutes,
+    seconds, milliseconds, and sign of the coordinate.
+    @ivar longitude: longitude
+    @type longitude: (int, int, int, int, sign) tuple specifying the degrees,
+    minutes, seconds, milliseconds, and sign of the coordinate.
+    @ivar altitude: altitude
+    @type altitude: float
+    @ivar size: size of the sphere
+    @type size: float
+    @ivar horizontal_precision: horizontal precision
+    @type horizontal_precision: float
+    @ivar vertical_precision: vertical precision
+    @type vertical_precision: float
+    @see: RFC 1876"""
+
+    __slots__ = ['latitude', 'longitude', 'altitude', 'size',
+                 'horizontal_precision', 'vertical_precision']
+
+    def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
+                 size=_default_size, hprec=_default_hprec,
+                 vprec=_default_vprec):
+        """Initialize a LOC record instance.
+
+        The parameters I{latitude} and I{longitude} may be either a 4-tuple
+        of integers specifying (degrees, minutes, seconds, milliseconds),
+        or they may be floating point values specifying the number of
+        degrees. The other parameters are floats. Size, horizontal precision,
+        and vertical precision are specified in centimeters."""
+
+        super(LOC, self).__init__(rdclass, rdtype)
+        if isinstance(latitude, int) or isinstance(latitude, long):
+            latitude = float(latitude)
+        if isinstance(latitude, float):
+            latitude = _float_to_tuple(latitude)
+        self.latitude = latitude
+        if isinstance(longitude, int) or isinstance(longitude, long):
+            longitude = float(longitude)
+        if isinstance(longitude, float):
+            longitude = _float_to_tuple(longitude)
+        self.longitude = longitude
+        self.altitude = float(altitude)
+        self.size = float(size)
+        self.horizontal_precision = float(hprec)
+        self.vertical_precision = float(vprec)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.latitude[4] > 0:
+            lat_hemisphere = 'N'
+        else:
+            lat_hemisphere = 'S'
+        if self.longitude[4] > 0:
+            long_hemisphere = 'E'
+        else:
+            long_hemisphere = 'W'
+        text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
+            self.latitude[0], self.latitude[1],
+            self.latitude[2], self.latitude[3], lat_hemisphere,
+            self.longitude[0], self.longitude[1], self.longitude[2],
+            self.longitude[3], long_hemisphere,
+            self.altitude / 100.0
+        )
+
+        # do not print default values
+        if self.size != _default_size or \
+            self.horizontal_precision != _default_hprec or \
+                self.vertical_precision != _default_vprec:
+            text += " {:0.2f}m {:0.2f}m {:0.2f}m".format(
+                self.size / 100.0, self.horizontal_precision / 100.0,
+                self.vertical_precision / 100.0
+            )
+        return text
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        latitude = [0, 0, 0, 0, 1]
+        longitude = [0, 0, 0, 0, 1]
+        size = _default_size
+        hprec = _default_hprec
+        vprec = _default_vprec
+
+        latitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            latitude[1] = int(t)
+            t = tok.get_string()
+            if '.' in t:
+                (seconds, milliseconds) = t.split('.')
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError(
+                        'bad latitude seconds value')
+                latitude[2] = int(seconds)
+                if latitude[2] >= 60:
+                    raise dns.exception.SyntaxError('latitude seconds >= 60')
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError(
+                        'bad latitude milliseconds value')
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                latitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                latitude[2] = int(t)
+                t = tok.get_string()
+        if t == 'S':
+            latitude[4] = -1
+        elif t != 'N':
+            raise dns.exception.SyntaxError('bad latitude hemisphere value')
+
+        longitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            longitude[1] = int(t)
+            t = tok.get_string()
+            if '.' in t:
+                (seconds, milliseconds) = t.split('.')
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError(
+                        'bad longitude seconds value')
+                longitude[2] = int(seconds)
+                if longitude[2] >= 60:
+                    raise dns.exception.SyntaxError('longitude seconds >= 60')
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError(
+                        'bad longitude milliseconds value')
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                longitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                longitude[2] = int(t)
+                t = tok.get_string()
+        if t == 'W':
+            longitude[4] = -1
+        elif t != 'E':
+            raise dns.exception.SyntaxError('bad longitude hemisphere value')
+
+        t = tok.get_string()
+        if t[-1] == 'm':
+            t = t[0: -1]
+        altitude = float(t) * 100.0        # m -> cm
+
+        token = tok.get().unescape()
+        if not token.is_eol_or_eof():
+            value = token.value
+            if value[-1] == 'm':
+                value = value[0: -1]
+            size = float(value) * 100.0        # m -> cm
+            token = tok.get().unescape()
+            if not token.is_eol_or_eof():
+                value = token.value
+                if value[-1] == 'm':
+                    value = value[0: -1]
+                hprec = float(value) * 100.0        # m -> cm
+                token = tok.get().unescape()
+                if not token.is_eol_or_eof():
+                    value = token.value
+                    if value[-1] == 'm':
+                        value = value[0: -1]
+                    vprec = float(value) * 100.0        # m -> cm
+                    tok.get_eol()
+
+        return cls(rdclass, rdtype, latitude, longitude, altitude,
+                   size, hprec, vprec)
+
+    def to_wire(self, file, compress=None, origin=None):
+        milliseconds = (self.latitude[0] * 3600000 +
+                        self.latitude[1] * 60000 +
+                        self.latitude[2] * 1000 +
+                        self.latitude[3]) * self.latitude[4]
+        latitude = long(0x80000000) + milliseconds
+        milliseconds = (self.longitude[0] * 3600000 +
+                        self.longitude[1] * 60000 +
+                        self.longitude[2] * 1000 +
+                        self.longitude[3]) * self.longitude[4]
+        longitude = long(0x80000000) + milliseconds
+        altitude = long(self.altitude) + long(10000000)
+        size = _encode_size(self.size, "size")
+        hprec = _encode_size(self.horizontal_precision, "horizontal precision")
+        vprec = _encode_size(self.vertical_precision, "vertical precision")
+        wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
+                           longitude, altitude)
+        file.write(wire)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (version, size, hprec, vprec, latitude, longitude, altitude) = \
+            struct.unpack("!BBBBIII", wire[current: current + rdlen])
+        if latitude > long(0x80000000):
+            latitude = float(latitude - long(0x80000000)) / 3600000
+        else:
+            latitude = -1 * float(long(0x80000000) - latitude) / 3600000
+        if latitude < -90.0 or latitude > 90.0:
+            raise dns.exception.FormError("bad latitude")
+        if longitude > long(0x80000000):
+            longitude = float(longitude - long(0x80000000)) / 3600000
+        else:
+            longitude = -1 * float(long(0x80000000) - longitude) / 3600000
+        if longitude < -180.0 or longitude > 180.0:
+            raise dns.exception.FormError("bad longitude")
+        altitude = float(altitude) - 10000000.0
+        size = _decode_size(size, "size")
+        hprec = _decode_size(hprec, "horizontal precision")
+        vprec = _decode_size(vprec, "vertical precision")
+        return cls(rdclass, rdtype, latitude, longitude, altitude,
+                   size, hprec, vprec)
+
+    def _get_float_latitude(self):
+        return _tuple_to_float(self.latitude)
+
+    def _set_float_latitude(self, value):
+        self.latitude = _float_to_tuple(value)
+
+    float_latitude = property(_get_float_latitude, _set_float_latitude,
+                              doc="latitude as a floating point value")
+
+    def _get_float_longitude(self):
+        return _tuple_to_float(self.longitude)
+
+    def _set_float_longitude(self, value):
+        self.longitude = _float_to_tuple(value)
+
+    float_longitude = property(_get_float_longitude, _set_float_longitude,
+                               doc="longitude as a floating point value")

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/MX.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+
+class MX(dns.rdtypes.mxbase.MXBase):
+
+    """MX record"""

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NS.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+
+class NS(dns.rdtypes.nsbase.NSBase):
+
+    """NS record"""

+ 128 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC.py

@@ -0,0 +1,128 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+import dns.name
+from dns._compat import xrange
+
+
+class NSEC(dns.rdata.Rdata):
+
+    """NSEC record
+
+    @ivar next: the next name
+    @type next: dns.name.Name object
+    @ivar windows: the windowed bitmap list
+    @type windows: list of (window number, string) tuples"""
+
+    __slots__ = ['next', 'windows']
+
+    def __init__(self, rdclass, rdtype, next, windows):
+        super(NSEC, self).__init__(rdclass, rdtype)
+        self.next = next
+        self.windows = windows
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = self.next.choose_relativity(origin, relativize)
+        text = ''
+        for (window, bitmap) in self.windows:
+            bits = []
+            for i in xrange(0, len(bitmap)):
+                byte = bitmap[i]
+                for j in xrange(0, 8):
+                    if byte & (0x80 >> j):
+                        bits.append(dns.rdatatype.to_text(window * 256 +
+                                                          i * 8 + j))
+            text += (' ' + ' '.join(bits))
+        return '{}{}'.format(next, text)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        next = tok.get_name()
+        next = next.choose_relativity(origin, relativize)
+        rdtypes = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("NSEC with bit 0")
+            if nrdtype > 65535:
+                raise dns.exception.SyntaxError("NSEC with bit > 65535")
+            rdtypes.append(nrdtype)
+        rdtypes.sort()
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = bytearray(b'\0' * 32)
+        windows = []
+        for nrdtype in rdtypes:
+            if nrdtype == prior_rdtype:
+                continue
+            prior_rdtype = nrdtype
+            new_window = nrdtype // 256
+            if new_window != window:
+                windows.append((window, bitmap[0:octets]))
+                bitmap = bytearray(b'\0' * 32)
+                window = new_window
+            offset = nrdtype % 256
+            byte = offset // 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = bitmap[byte] | (0x80 >> bit)
+
+        windows.append((window, bitmap[0:octets]))
+        return cls(rdclass, rdtype, next, windows)
+
+    def to_wire(self, file, compress=None, origin=None):
+        self.next.to_wire(file, None, origin)
+        for (window, bitmap) in self.windows:
+            file.write(struct.pack('!BB', window, len(bitmap)))
+            file.write(bitmap)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (next, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        windows = []
+        while rdlen > 0:
+            if rdlen < 3:
+                raise dns.exception.FormError("NSEC too short")
+            window = wire[current]
+            octets = wire[current + 1]
+            if octets == 0 or octets > 32:
+                raise dns.exception.FormError("bad NSEC octets")
+            current += 2
+            rdlen -= 2
+            if rdlen < octets:
+                raise dns.exception.FormError("bad NSEC bitmap length")
+            bitmap = bytearray(wire[current: current + octets].unwrap())
+            current += octets
+            rdlen -= octets
+            windows.append((window, bitmap))
+        if origin is not None:
+            next = next.relativize(origin)
+        return cls(rdclass, rdtype, next, windows)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.next = self.next.choose_relativity(origin, relativize)

+ 196 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC3.py

@@ -0,0 +1,196 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+import binascii
+import string
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+from dns._compat import xrange, text_type, PY3
+
+# pylint: disable=deprecated-string-function
+if PY3:
+    b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV',
+                                        b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
+    b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
+                                        b'0123456789ABCDEFGHIJKLMNOPQRSTUV')
+else:
+    b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV',
+                                         'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
+    b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
+                                         '0123456789ABCDEFGHIJKLMNOPQRSTUV')
+# pylint: enable=deprecated-string-function
+
+
+# hash algorithm constants
+SHA1 = 1
+
+# flag constants
+OPTOUT = 1
+
+
+class NSEC3(dns.rdata.Rdata):
+
+    """NSEC3 record
+
+    @ivar algorithm: the hash algorithm number
+    @type algorithm: int
+    @ivar flags: the flags
+    @type flags: int
+    @ivar iterations: the number of iterations
+    @type iterations: int
+    @ivar salt: the salt
+    @type salt: string
+    @ivar next: the next name hash
+    @type next: string
+    @ivar windows: the windowed bitmap list
+    @type windows: list of (window number, string) tuples"""
+
+    __slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows']
+
+    def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt,
+                 next, windows):
+        super(NSEC3, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.flags = flags
+        self.iterations = iterations
+        if isinstance(salt, text_type):
+            self.salt = salt.encode()
+        else:
+            self.salt = salt
+        self.next = next
+        self.windows = windows
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = base64.b32encode(self.next).translate(
+            b32_normal_to_hex).lower().decode()
+        if self.salt == b'':
+            salt = '-'
+        else:
+            salt = binascii.hexlify(self.salt).decode()
+        text = u''
+        for (window, bitmap) in self.windows:
+            bits = []
+            for i in xrange(0, len(bitmap)):
+                byte = bitmap[i]
+                for j in xrange(0, 8):
+                    if byte & (0x80 >> j):
+                        bits.append(dns.rdatatype.to_text(window * 256 +
+                                                          i * 8 + j))
+            text += (u' ' + u' '.join(bits))
+        return u'%u %u %u %s %s%s' % (self.algorithm, self.flags,
+                                      self.iterations, salt, next, text)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == u'-':
+            salt = b''
+        else:
+            salt = binascii.unhexlify(salt.encode('ascii'))
+        next = tok.get_string().encode(
+            'ascii').upper().translate(b32_hex_to_normal)
+        next = base64.b32decode(next)
+        rdtypes = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            nrdtype = dns.rdatatype.from_text(token.value)
+            if nrdtype == 0:
+                raise dns.exception.SyntaxError("NSEC3 with bit 0")
+            if nrdtype > 65535:
+                raise dns.exception.SyntaxError("NSEC3 with bit > 65535")
+            rdtypes.append(nrdtype)
+        rdtypes.sort()
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = bytearray(b'\0' * 32)
+        windows = []
+        for nrdtype in rdtypes:
+            if nrdtype == prior_rdtype:
+                continue
+            prior_rdtype = nrdtype
+            new_window = nrdtype // 256
+            if new_window != window:
+                if octets != 0:
+                    windows.append((window, bitmap[0:octets]))
+                bitmap = bytearray(b'\0' * 32)
+                window = new_window
+            offset = nrdtype % 256
+            byte = offset // 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = bitmap[byte] | (0x80 >> bit)
+        if octets != 0:
+            windows.append((window, bitmap[0:octets]))
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next,
+                   windows)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags,
+                               self.iterations, l))
+        file.write(self.salt)
+        l = len(self.next)
+        file.write(struct.pack("!B", l))
+        file.write(self.next)
+        for (window, bitmap) in self.windows:
+            file.write(struct.pack("!BB", window, len(bitmap)))
+            file.write(bitmap)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (algorithm, flags, iterations, slen) = \
+            struct.unpack('!BBHB', wire[current: current + 5])
+
+        current += 5
+        rdlen -= 5
+        salt = wire[current: current + slen].unwrap()
+        current += slen
+        rdlen -= slen
+        nlen = wire[current]
+        current += 1
+        rdlen -= 1
+        next = wire[current: current + nlen].unwrap()
+        current += nlen
+        rdlen -= nlen
+        windows = []
+        while rdlen > 0:
+            if rdlen < 3:
+                raise dns.exception.FormError("NSEC3 too short")
+            window = wire[current]
+            octets = wire[current + 1]
+            if octets == 0 or octets > 32:
+                raise dns.exception.FormError("bad NSEC3 octets")
+            current += 2
+            rdlen -= 2
+            if rdlen < octets:
+                raise dns.exception.FormError("bad NSEC3 bitmap length")
+            bitmap = bytearray(wire[current: current + octets].unwrap())
+            current += octets
+            rdlen -= octets
+            windows.append((window, bitmap))
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next,
+                   windows)

+ 90 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/NSEC3PARAM.py

@@ -0,0 +1,90 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import binascii
+
+import dns.exception
+import dns.rdata
+from dns._compat import text_type
+
+
+class NSEC3PARAM(dns.rdata.Rdata):
+
+    """NSEC3PARAM record
+
+    @ivar algorithm: the hash algorithm number
+    @type algorithm: int
+    @ivar flags: the flags
+    @type flags: int
+    @ivar iterations: the number of iterations
+    @type iterations: int
+    @ivar salt: the salt
+    @type salt: string"""
+
+    __slots__ = ['algorithm', 'flags', 'iterations', 'salt']
+
+    def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt):
+        super(NSEC3PARAM, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.flags = flags
+        self.iterations = iterations
+        if isinstance(salt, text_type):
+            self.salt = salt.encode()
+        else:
+            self.salt = salt
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.salt == b'':
+            salt = '-'
+        else:
+            salt = binascii.hexlify(self.salt).decode()
+        return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations,
+                                salt)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == '-':
+            salt = ''
+        else:
+            salt = binascii.unhexlify(salt.encode())
+        tok.get_eol()
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags,
+                               self.iterations, l))
+        file.write(self.salt)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (algorithm, flags, iterations, slen) = \
+             struct.unpack('!BBHB',
+                           wire[current: current + 5])
+        current += 5
+        rdlen -= 5
+        salt = wire[current: current + slen].unwrap()
+        current += slen
+        rdlen -= slen
+        if rdlen != 0:
+            raise dns.exception.FormError
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)

+ 60 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/OPENPGPKEY.py

@@ -0,0 +1,60 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2016 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+class OPENPGPKEY(dns.rdata.Rdata):
+
+    """OPENPGPKEY record
+
+    @ivar key: the key
+    @type key: bytes
+    @see: RFC 7929
+    """
+
+    def __init__(self, rdclass, rdtype, key):
+        super(OPENPGPKEY, self).__init__(rdclass, rdtype)
+        self.key = key
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._base64ify(self.key)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        key = base64.b64decode(b64)
+        return cls(rdclass, rdtype, key)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(self.key)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        key = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, key)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/PTR.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+
+class PTR(dns.rdtypes.nsbase.NSBase):
+
+    """PTR record"""

+ 82 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RP.py

@@ -0,0 +1,82 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+
+class RP(dns.rdata.Rdata):
+
+    """RP record
+
+    @ivar mbox: The responsible person's mailbox
+    @type mbox: dns.name.Name object
+    @ivar txt: The owner name of a node with TXT records, or the root name
+    if no TXT records are associated with this RP.
+    @type txt: dns.name.Name object
+    @see: RFC 1183"""
+
+    __slots__ = ['mbox', 'txt']
+
+    def __init__(self, rdclass, rdtype, mbox, txt):
+        super(RP, self).__init__(rdclass, rdtype)
+        self.mbox = mbox
+        self.txt = txt
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mbox = self.mbox.choose_relativity(origin, relativize)
+        txt = self.txt.choose_relativity(origin, relativize)
+        return "{} {}".format(str(mbox), str(txt))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        mbox = tok.get_name()
+        txt = tok.get_name()
+        mbox = mbox.choose_relativity(origin, relativize)
+        txt = txt.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, mbox, txt)
+
+    def to_wire(self, file, compress=None, origin=None):
+        self.mbox.to_wire(file, None, origin)
+        self.txt.to_wire(file, None, origin)
+
+    def to_digestable(self, origin=None):
+        return self.mbox.to_digestable(origin) + \
+            self.txt.to_digestable(origin)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (mbox, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                           current)
+        current += cused
+        rdlen -= cused
+        if rdlen <= 0:
+            raise dns.exception.FormError
+        (txt, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                          current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if origin is not None:
+            mbox = mbox.relativize(origin)
+            txt = txt.relativize(origin)
+        return cls(rdclass, rdtype, mbox, txt)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.mbox = self.mbox.choose_relativity(origin, relativize)
+        self.txt = self.txt.choose_relativity(origin, relativize)

+ 158 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RRSIG.py

@@ -0,0 +1,158 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+import calendar
+import struct
+import time
+
+import dns.dnssec
+import dns.exception
+import dns.rdata
+import dns.rdatatype
+
+
+class BadSigTime(dns.exception.DNSException):
+
+    """Time in DNS SIG or RRSIG resource record cannot be parsed."""
+
+
+def sigtime_to_posixtime(what):
+    if len(what) != 14:
+        raise BadSigTime
+    year = int(what[0:4])
+    month = int(what[4:6])
+    day = int(what[6:8])
+    hour = int(what[8:10])
+    minute = int(what[10:12])
+    second = int(what[12:14])
+    return calendar.timegm((year, month, day, hour, minute, second,
+                            0, 0, 0))
+
+
+def posixtime_to_sigtime(what):
+    return time.strftime('%Y%m%d%H%M%S', time.gmtime(what))
+
+
+class RRSIG(dns.rdata.Rdata):
+
+    """RRSIG record
+
+    @ivar type_covered: the rdata type this signature covers
+    @type type_covered: int
+    @ivar algorithm: the algorithm used for the sig
+    @type algorithm: int
+    @ivar labels: number of labels
+    @type labels: int
+    @ivar original_ttl: the original TTL
+    @type original_ttl: long
+    @ivar expiration: signature expiration time
+    @type expiration: long
+    @ivar inception: signature inception time
+    @type inception: long
+    @ivar key_tag: the key tag
+    @type key_tag: int
+    @ivar signer: the signer
+    @type signer: dns.name.Name object
+    @ivar signature: the signature
+    @type signature: string"""
+
+    __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl',
+                 'expiration', 'inception', 'key_tag', 'signer',
+                 'signature']
+
+    def __init__(self, rdclass, rdtype, type_covered, algorithm, labels,
+                 original_ttl, expiration, inception, key_tag, signer,
+                 signature):
+        super(RRSIG, self).__init__(rdclass, rdtype)
+        self.type_covered = type_covered
+        self.algorithm = algorithm
+        self.labels = labels
+        self.original_ttl = original_ttl
+        self.expiration = expiration
+        self.inception = inception
+        self.key_tag = key_tag
+        self.signer = signer
+        self.signature = signature
+
+    def covers(self):
+        return self.type_covered
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%s %d %d %d %s %s %d %s %s' % (
+            dns.rdatatype.to_text(self.type_covered),
+            self.algorithm,
+            self.labels,
+            self.original_ttl,
+            posixtime_to_sigtime(self.expiration),
+            posixtime_to_sigtime(self.inception),
+            self.key_tag,
+            self.signer.choose_relativity(origin, relativize),
+            dns.rdata._base64ify(self.signature)
+        )
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        type_covered = dns.rdatatype.from_text(tok.get_string())
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        labels = tok.get_int()
+        original_ttl = tok.get_ttl()
+        expiration = sigtime_to_posixtime(tok.get_string())
+        inception = sigtime_to_posixtime(tok.get_string())
+        key_tag = tok.get_int()
+        signer = tok.get_name()
+        signer = signer.choose_relativity(origin, relativize)
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        signature = base64.b64decode(b64)
+        return cls(rdclass, rdtype, type_covered, algorithm, labels,
+                   original_ttl, expiration, inception, key_tag, signer,
+                   signature)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack('!HBBIIIH', self.type_covered,
+                             self.algorithm, self.labels,
+                             self.original_ttl, self.expiration,
+                             self.inception, self.key_tag)
+        file.write(header)
+        self.signer.to_wire(file, None, origin)
+        file.write(self.signature)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        header = struct.unpack('!HBBIIIH', wire[current: current + 18])
+        current += 18
+        rdlen -= 18
+        (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        if origin is not None:
+            signer = signer.relativize(origin)
+        signature = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], header[1], header[2],
+                   header[3], header[4], header[5], header[6], signer,
+                   signature)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.signer = self.signer.choose_relativity(origin, relativize)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/RT.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+
+class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+
+    """RT record"""

+ 116 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SOA.py

@@ -0,0 +1,116 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+
+class SOA(dns.rdata.Rdata):
+
+    """SOA record
+
+    @ivar mname: the SOA MNAME (master name) field
+    @type mname: dns.name.Name object
+    @ivar rname: the SOA RNAME (responsible name) field
+    @type rname: dns.name.Name object
+    @ivar serial: The zone's serial number
+    @type serial: int
+    @ivar refresh: The zone's refresh value (in seconds)
+    @type refresh: int
+    @ivar retry: The zone's retry value (in seconds)
+    @type retry: int
+    @ivar expire: The zone's expiration value (in seconds)
+    @type expire: int
+    @ivar minimum: The zone's negative caching time (in seconds, called
+    "minimum" for historical reasons)
+    @type minimum: int
+    @see: RFC 1035"""
+
+    __slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire',
+                 'minimum']
+
+    def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry,
+                 expire, minimum):
+        super(SOA, self).__init__(rdclass, rdtype)
+        self.mname = mname
+        self.rname = rname
+        self.serial = serial
+        self.refresh = refresh
+        self.retry = retry
+        self.expire = expire
+        self.minimum = minimum
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mname = self.mname.choose_relativity(origin, relativize)
+        rname = self.rname.choose_relativity(origin, relativize)
+        return '%s %s %d %d %d %d %d' % (
+            mname, rname, self.serial, self.refresh, self.retry,
+            self.expire, self.minimum)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        mname = tok.get_name()
+        rname = tok.get_name()
+        mname = mname.choose_relativity(origin, relativize)
+        rname = rname.choose_relativity(origin, relativize)
+        serial = tok.get_uint32()
+        refresh = tok.get_ttl()
+        retry = tok.get_ttl()
+        expire = tok.get_ttl()
+        minimum = tok.get_ttl()
+        tok.get_eol()
+        return cls(rdclass, rdtype, mname, rname, serial, refresh, retry,
+                   expire, minimum)
+
+    def to_wire(self, file, compress=None, origin=None):
+        self.mname.to_wire(file, compress, origin)
+        self.rname.to_wire(file, compress, origin)
+        five_ints = struct.pack('!IIIII', self.serial, self.refresh,
+                                self.retry, self.expire, self.minimum)
+        file.write(five_ints)
+
+    def to_digestable(self, origin=None):
+        return self.mname.to_digestable(origin) + \
+            self.rname.to_digestable(origin) + \
+            struct.pack('!IIIII', self.serial, self.refresh,
+                        self.retry, self.expire, self.minimum)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current)
+        current += cused
+        rdlen -= cused
+        if rdlen != 20:
+            raise dns.exception.FormError
+        five_ints = struct.unpack('!IIIII',
+                                  wire[current: current + rdlen])
+        if origin is not None:
+            mname = mname.relativize(origin)
+            rname = rname.relativize(origin)
+        return cls(rdclass, rdtype, mname, rname,
+                   five_ints[0], five_ints[1], five_ints[2], five_ints[3],
+                   five_ints[4])
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.mname = self.mname.choose_relativity(origin, relativize)
+        self.rname = self.rname.choose_relativity(origin, relativize)

+ 25 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SPF.py

@@ -0,0 +1,25 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.txtbase
+
+
+class SPF(dns.rdtypes.txtbase.TXTBase):
+
+    """SPF record
+
+    @see: RFC 4408"""

+ 79 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/SSHFP.py

@@ -0,0 +1,79 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import binascii
+
+import dns.rdata
+import dns.rdatatype
+
+
+class SSHFP(dns.rdata.Rdata):
+
+    """SSHFP record
+
+    @ivar algorithm: the algorithm
+    @type algorithm: int
+    @ivar fp_type: the digest type
+    @type fp_type: int
+    @ivar fingerprint: the fingerprint
+    @type fingerprint: string
+    @see: draft-ietf-secsh-dns-05.txt"""
+
+    __slots__ = ['algorithm', 'fp_type', 'fingerprint']
+
+    def __init__(self, rdclass, rdtype, algorithm, fp_type,
+                 fingerprint):
+        super(SSHFP, self).__init__(rdclass, rdtype)
+        self.algorithm = algorithm
+        self.fp_type = fp_type
+        self.fingerprint = fingerprint
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %s' % (self.algorithm,
+                             self.fp_type,
+                             dns.rdata._hexify(self.fingerprint,
+                                               chunksize=128))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        algorithm = tok.get_uint8()
+        fp_type = tok.get_uint8()
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        fingerprint = b''.join(chunks)
+        fingerprint = binascii.unhexlify(fingerprint)
+        return cls(rdclass, rdtype, algorithm, fp_type, fingerprint)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack("!BB", self.algorithm, self.fp_type)
+        file.write(header)
+        file.write(self.fingerprint)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        header = struct.unpack("!BB", wire[current: current + 2])
+        current += 2
+        rdlen -= 2
+        fingerprint = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], header[1], fingerprint)

+ 84 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/TLSA.py

@@ -0,0 +1,84 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import binascii
+
+import dns.rdata
+import dns.rdatatype
+
+
+class TLSA(dns.rdata.Rdata):
+
+    """TLSA record
+
+    @ivar usage: The certificate usage
+    @type usage: int
+    @ivar selector: The selector field
+    @type selector: int
+    @ivar mtype: The 'matching type' field
+    @type mtype: int
+    @ivar cert: The 'Certificate Association Data' field
+    @type cert: string
+    @see: RFC 6698"""
+
+    __slots__ = ['usage', 'selector', 'mtype', 'cert']
+
+    def __init__(self, rdclass, rdtype, usage, selector,
+                 mtype, cert):
+        super(TLSA, self).__init__(rdclass, rdtype)
+        self.usage = usage
+        self.selector = selector
+        self.mtype = mtype
+        self.cert = cert
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %d %s' % (self.usage,
+                                self.selector,
+                                self.mtype,
+                                dns.rdata._hexify(self.cert,
+                                                  chunksize=128))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        usage = tok.get_uint8()
+        selector = tok.get_uint8()
+        mtype = tok.get_uint8()
+        cert_chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            cert_chunks.append(t.value.encode())
+        cert = b''.join(cert_chunks)
+        cert = binascii.unhexlify(cert)
+        return cls(rdclass, rdtype, usage, selector, mtype, cert)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack("!BBB", self.usage, self.selector, self.mtype)
+        file.write(header)
+        file.write(self.cert)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        header = struct.unpack("!BBB", wire[current: current + 3])
+        current += 3
+        rdlen -= 3
+        cert = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], cert)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/TXT.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.txtbase
+
+
+class TXT(dns.rdtypes.txtbase.TXTBase):
+
+    """TXT record"""

+ 82 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/URI.py

@@ -0,0 +1,82 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+from dns._compat import text_type
+
+
+class URI(dns.rdata.Rdata):
+
+    """URI record
+
+    @ivar priority: the priority
+    @type priority: int
+    @ivar weight: the weight
+    @type weight: int
+    @ivar target: the target host
+    @type target: dns.name.Name object
+    @see: draft-faltstrom-uri-13"""
+
+    __slots__ = ['priority', 'weight', 'target']
+
+    def __init__(self, rdclass, rdtype, priority, weight, target):
+        super(URI, self).__init__(rdclass, rdtype)
+        self.priority = priority
+        self.weight = weight
+        if len(target) < 1:
+            raise dns.exception.SyntaxError("URI target cannot be empty")
+        if isinstance(target, text_type):
+            self.target = target.encode()
+        else:
+            self.target = target
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d "%s"' % (self.priority, self.weight,
+                               self.target.decode())
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        priority = tok.get_uint16()
+        weight = tok.get_uint16()
+        target = tok.get().unescape()
+        if not (target.is_quoted_string() or target.is_identifier()):
+            raise dns.exception.SyntaxError("URI target must be a string")
+        tok.get_eol()
+        return cls(rdclass, rdtype, priority, weight, target.value)
+
+    def to_wire(self, file, compress=None, origin=None):
+        two_ints = struct.pack("!HH", self.priority, self.weight)
+        file.write(two_ints)
+        file.write(self.target)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        if rdlen < 5:
+            raise dns.exception.FormError('URI RR is shorter than 5 octets')
+
+        (priority, weight) = struct.unpack('!HH', wire[current: current + 4])
+        current += 4
+        rdlen -= 4
+        target = wire[current: current + rdlen]
+        current += rdlen
+
+        return cls(rdclass, rdtype, priority, weight, target)

+ 66 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/X25.py

@@ -0,0 +1,66 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+from dns._compat import text_type
+
+
+class X25(dns.rdata.Rdata):
+
+    """X25 record
+
+    @ivar address: the PSDN address
+    @type address: string
+    @see: RFC 1183"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(X25, self).__init__(rdclass, rdtype)
+        if isinstance(address, text_type):
+            self.address = address.encode()
+        else:
+            self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '"%s"' % dns.rdata._escapify(self.address)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_string()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+
+    def to_wire(self, file, compress=None, origin=None):
+        l = len(self.address)
+        assert l < 256
+        file.write(struct.pack('!B', l))
+        file.write(self.address)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        l = wire[current]
+        current += 1
+        rdlen -= 1
+        if l != rdlen:
+            raise dns.exception.FormError
+        address = wire[current: current + l].unwrap()
+        return cls(rdclass, rdtype, address)

+ 57 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/ANY/__init__.py

@@ -0,0 +1,57 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Class ANY (generic) rdata type classes."""
+
+__all__ = [
+    'AFSDB',
+    'AVC',
+    'CAA',
+    'CDNSKEY',
+    'CDS',
+    'CERT',
+    'CNAME',
+    'CSYNC',
+    'DLV',
+    'DNAME',
+    'DNSKEY',
+    'DS',
+    'EUI48',
+    'EUI64',
+    'GPOS',
+    'HINFO',
+    'HIP',
+    'ISDN',
+    'LOC',
+    'MX',
+    'NS',
+    'NSEC',
+    'NSEC3',
+    'NSEC3PARAM',
+    'OPENPGPKEY',
+    'PTR',
+    'RP',
+    'RRSIG',
+    'RT',
+    'SOA',
+    'SPF',
+    'SSHFP',
+    'TLSA',
+    'TXT',
+    'URI',
+    'X25',
+]

+ 70 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/CH/A.py

@@ -0,0 +1,70 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+import struct
+
+class A(dns.rdtypes.mxbase.MXBase):
+
+    """A record for Chaosnet
+    @ivar domain: the domain of the address
+    @type domain: dns.name.Name object
+    @ivar address: the 16-bit address
+    @type address: int"""
+
+    __slots__ = ['domain', 'address']
+
+    def __init__(self, rdclass, rdtype, address, domain):
+        super(A, self).__init__(rdclass, rdtype, address, domain)
+        self.domain = domain
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        domain = self.domain.choose_relativity(origin, relativize)
+        return '%s %o' % (domain, self.address)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        domain = tok.get_name()
+        address = tok.get_uint16(base=8)
+        domain = domain.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, address, domain)
+
+    def to_wire(self, file, compress=None, origin=None):
+        self.domain.to_wire(file, compress, origin)
+        pref = struct.pack("!H", self.address)
+        file.write(pref)
+
+    def to_digestable(self, origin=None):
+        return self.domain.to_digestable(origin) + \
+            struct.pack("!H", self.address)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (domain, cused) = dns.name.from_wire(wire[: current + rdlen-2],
+                                               current)
+        current += cused
+        (address,) = struct.unpack('!H', wire[current: current + 2])
+        if cused+2 != rdlen:
+            raise dns.exception.FormError
+        if origin is not None:
+            domain = domain.relativize(origin)
+        return cls(rdclass, rdtype, address, domain)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.domain = self.domain.choose_relativity(origin, relativize)

+ 22 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/CH/__init__.py

@@ -0,0 +1,22 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Class CH rdata type classes."""
+
+__all__ = [
+    'A',
+]

+ 54 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/A.py

@@ -0,0 +1,54 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.ipv4
+import dns.rdata
+import dns.tokenizer
+
+
+class A(dns.rdata.Rdata):
+
+    """A record.
+
+    @ivar address: an IPv4 address
+    @type address: string (in the standard "dotted quad" format)"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(A, self).__init__(rdclass, rdtype)
+        # check that it's OK
+        dns.ipv4.inet_aton(address)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_identifier()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(dns.ipv4.inet_aton(self.address))
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        address = dns.ipv4.inet_ntoa(wire[current: current + rdlen])
+        return cls(rdclass, rdtype, address)

+ 55 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/AAAA.py

@@ -0,0 +1,55 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.exception
+import dns.inet
+import dns.rdata
+import dns.tokenizer
+
+
+class AAAA(dns.rdata.Rdata):
+
+    """AAAA record.
+
+    @ivar address: an IPv6 address
+    @type address: string (in the standard IPv6 format)"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(AAAA, self).__init__(rdclass, rdtype)
+        # check that it's OK
+        dns.inet.inet_pton(dns.inet.AF_INET6, address)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_identifier()
+        tok.get_eol()
+        return cls(rdclass, rdtype, address)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address))
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        address = dns.inet.inet_ntop(dns.inet.AF_INET6,
+                                     wire[current: current + rdlen])
+        return cls(rdclass, rdtype, address)

+ 165 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/APL.py

@@ -0,0 +1,165 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import binascii
+import codecs
+import struct
+
+import dns.exception
+import dns.inet
+import dns.rdata
+import dns.tokenizer
+from dns._compat import xrange, maybe_chr
+
+
+class APLItem(object):
+
+    """An APL list item.
+
+    @ivar family: the address family (IANA address family registry)
+    @type family: int
+    @ivar negation: is this item negated?
+    @type negation: bool
+    @ivar address: the address
+    @type address: string
+    @ivar prefix: the prefix length
+    @type prefix: int
+    """
+
+    __slots__ = ['family', 'negation', 'address', 'prefix']
+
+    def __init__(self, family, negation, address, prefix):
+        self.family = family
+        self.negation = negation
+        self.address = address
+        self.prefix = prefix
+
+    def __str__(self):
+        if self.negation:
+            return "!%d:%s/%s" % (self.family, self.address, self.prefix)
+        else:
+            return "%d:%s/%s" % (self.family, self.address, self.prefix)
+
+    def to_wire(self, file):
+        if self.family == 1:
+            address = dns.inet.inet_pton(dns.inet.AF_INET, self.address)
+        elif self.family == 2:
+            address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address)
+        else:
+            address = binascii.unhexlify(self.address)
+        #
+        # Truncate least significant zero bytes.
+        #
+        last = 0
+        for i in xrange(len(address) - 1, -1, -1):
+            if address[i] != maybe_chr(0):
+                last = i + 1
+                break
+        address = address[0: last]
+        l = len(address)
+        assert l < 128
+        if self.negation:
+            l |= 0x80
+        header = struct.pack('!HBB', self.family, self.prefix, l)
+        file.write(header)
+        file.write(address)
+
+
+class APL(dns.rdata.Rdata):
+
+    """APL record.
+
+    @ivar items: a list of APL items
+    @type items: list of APL_Item
+    @see: RFC 3123"""
+
+    __slots__ = ['items']
+
+    def __init__(self, rdclass, rdtype, items):
+        super(APL, self).__init__(rdclass, rdtype)
+        self.items = items
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return ' '.join(map(str, self.items))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        items = []
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            item = token.value
+            if item[0] == '!':
+                negation = True
+                item = item[1:]
+            else:
+                negation = False
+            (family, rest) = item.split(':', 1)
+            family = int(family)
+            (address, prefix) = rest.split('/', 1)
+            prefix = int(prefix)
+            item = APLItem(family, negation, address, prefix)
+            items.append(item)
+
+        return cls(rdclass, rdtype, items)
+
+    def to_wire(self, file, compress=None, origin=None):
+        for item in self.items:
+            item.to_wire(file)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+
+        items = []
+        while 1:
+            if rdlen == 0:
+                break
+            if rdlen < 4:
+                raise dns.exception.FormError
+            header = struct.unpack('!HBB', wire[current: current + 4])
+            afdlen = header[2]
+            if afdlen > 127:
+                negation = True
+                afdlen -= 128
+            else:
+                negation = False
+            current += 4
+            rdlen -= 4
+            if rdlen < afdlen:
+                raise dns.exception.FormError
+            address = wire[current: current + afdlen].unwrap()
+            l = len(address)
+            if header[0] == 1:
+                if l < 4:
+                    address += b'\x00' * (4 - l)
+                address = dns.inet.inet_ntop(dns.inet.AF_INET, address)
+            elif header[0] == 2:
+                if l < 16:
+                    address += b'\x00' * (16 - l)
+                address = dns.inet.inet_ntop(dns.inet.AF_INET6, address)
+            else:
+                #
+                # This isn't really right according to the RFC, but it
+                # seems better than throwing an exception
+                #
+                address = codecs.encode(address, 'hex_codec')
+            current += afdlen
+            rdlen -= afdlen
+            item = APLItem(header[0], negation, address, header[1])
+            items.append(item)
+        return cls(rdclass, rdtype, items)

+ 61 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/DHCID.py

@@ -0,0 +1,61 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+
+import dns.exception
+
+
+class DHCID(dns.rdata.Rdata):
+
+    """DHCID record
+
+    @ivar data: the data (the content of the RR is opaque as far as the
+    DNS is concerned)
+    @type data: string
+    @see: RFC 4701"""
+
+    __slots__ = ['data']
+
+    def __init__(self, rdclass, rdtype, data):
+        super(DHCID, self).__init__(rdclass, rdtype)
+        self.data = data
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._base64ify(self.data)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        data = base64.b64decode(b64)
+        return cls(rdclass, rdtype, data)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(self.data)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        data = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, data)

+ 150 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/IPSECKEY.py

@@ -0,0 +1,150 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import base64
+
+import dns.exception
+import dns.inet
+import dns.name
+
+
+class IPSECKEY(dns.rdata.Rdata):
+
+    """IPSECKEY record
+
+    @ivar precedence: the precedence for this key data
+    @type precedence: int
+    @ivar gateway_type: the gateway type
+    @type gateway_type: int
+    @ivar algorithm: the algorithm to use
+    @type algorithm: int
+    @ivar gateway: the public key
+    @type gateway: None, IPv4 address, IPV6 address, or domain name
+    @ivar key: the public key
+    @type key: string
+    @see: RFC 4025"""
+
+    __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key']
+
+    def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm,
+                 gateway, key):
+        super(IPSECKEY, self).__init__(rdclass, rdtype)
+        if gateway_type == 0:
+            if gateway != '.' and gateway is not None:
+                raise SyntaxError('invalid gateway for gateway type 0')
+            gateway = None
+        elif gateway_type == 1:
+            # check that it's OK
+            dns.inet.inet_pton(dns.inet.AF_INET, gateway)
+        elif gateway_type == 2:
+            # check that it's OK
+            dns.inet.inet_pton(dns.inet.AF_INET6, gateway)
+        elif gateway_type == 3:
+            pass
+        else:
+            raise SyntaxError(
+                'invalid IPSECKEY gateway type: %d' % gateway_type)
+        self.precedence = precedence
+        self.gateway_type = gateway_type
+        self.algorithm = algorithm
+        self.gateway = gateway
+        self.key = key
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.gateway_type == 0:
+            gateway = '.'
+        elif self.gateway_type == 1:
+            gateway = self.gateway
+        elif self.gateway_type == 2:
+            gateway = self.gateway
+        elif self.gateway_type == 3:
+            gateway = str(self.gateway.choose_relativity(origin, relativize))
+        else:
+            raise ValueError('invalid gateway type')
+        return '%d %d %d %s %s' % (self.precedence, self.gateway_type,
+                                   self.algorithm, gateway,
+                                   dns.rdata._base64ify(self.key))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        precedence = tok.get_uint8()
+        gateway_type = tok.get_uint8()
+        algorithm = tok.get_uint8()
+        if gateway_type == 3:
+            gateway = tok.get_name().choose_relativity(origin, relativize)
+        else:
+            gateway = tok.get_string()
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        key = base64.b64decode(b64)
+        return cls(rdclass, rdtype, precedence, gateway_type, algorithm,
+                   gateway, key)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack("!BBB", self.precedence, self.gateway_type,
+                             self.algorithm)
+        file.write(header)
+        if self.gateway_type == 0:
+            pass
+        elif self.gateway_type == 1:
+            file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway))
+        elif self.gateway_type == 2:
+            file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway))
+        elif self.gateway_type == 3:
+            self.gateway.to_wire(file, None, origin)
+        else:
+            raise ValueError('invalid gateway type')
+        file.write(self.key)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        if rdlen < 3:
+            raise dns.exception.FormError
+        header = struct.unpack('!BBB', wire[current: current + 3])
+        gateway_type = header[1]
+        current += 3
+        rdlen -= 3
+        if gateway_type == 0:
+            gateway = None
+        elif gateway_type == 1:
+            gateway = dns.inet.inet_ntop(dns.inet.AF_INET,
+                                         wire[current: current + 4])
+            current += 4
+            rdlen -= 4
+        elif gateway_type == 2:
+            gateway = dns.inet.inet_ntop(dns.inet.AF_INET6,
+                                         wire[current: current + 16])
+            current += 16
+            rdlen -= 16
+        elif gateway_type == 3:
+            (gateway, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                  current)
+            current += cused
+            rdlen -= cused
+        else:
+            raise dns.exception.FormError('invalid IPSECKEY gateway type')
+        key = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], gateway_type, header[2],
+                   gateway, key)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/KX.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.mxbase
+
+
+class KX(dns.rdtypes.mxbase.UncompressedMX):
+
+    """KX record"""

+ 127 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NAPTR.py

@@ -0,0 +1,127 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.name
+import dns.rdata
+from dns._compat import xrange, text_type
+
+
+def _write_string(file, s):
+    l = len(s)
+    assert l < 256
+    file.write(struct.pack('!B', l))
+    file.write(s)
+
+
+def _sanitize(value):
+    if isinstance(value, text_type):
+        return value.encode()
+    return value
+
+
+class NAPTR(dns.rdata.Rdata):
+
+    """NAPTR record
+
+    @ivar order: order
+    @type order: int
+    @ivar preference: preference
+    @type preference: int
+    @ivar flags: flags
+    @type flags: string
+    @ivar service: service
+    @type service: string
+    @ivar regexp: regular expression
+    @type regexp: string
+    @ivar replacement: replacement name
+    @type replacement: dns.name.Name object
+    @see: RFC 3403"""
+
+    __slots__ = ['order', 'preference', 'flags', 'service', 'regexp',
+                 'replacement']
+
+    def __init__(self, rdclass, rdtype, order, preference, flags, service,
+                 regexp, replacement):
+        super(NAPTR, self).__init__(rdclass, rdtype)
+        self.flags = _sanitize(flags)
+        self.service = _sanitize(service)
+        self.regexp = _sanitize(regexp)
+        self.order = order
+        self.preference = preference
+        self.replacement = replacement
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        replacement = self.replacement.choose_relativity(origin, relativize)
+        return '%d %d "%s" "%s" "%s" %s' % \
+               (self.order, self.preference,
+                dns.rdata._escapify(self.flags),
+                dns.rdata._escapify(self.service),
+                dns.rdata._escapify(self.regexp),
+                replacement)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        order = tok.get_uint16()
+        preference = tok.get_uint16()
+        flags = tok.get_string()
+        service = tok.get_string()
+        regexp = tok.get_string()
+        replacement = tok.get_name()
+        replacement = replacement.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, order, preference, flags, service,
+                   regexp, replacement)
+
+    def to_wire(self, file, compress=None, origin=None):
+        two_ints = struct.pack("!HH", self.order, self.preference)
+        file.write(two_ints)
+        _write_string(file, self.flags)
+        _write_string(file, self.service)
+        _write_string(file, self.regexp)
+        self.replacement.to_wire(file, compress, origin)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (order, preference) = struct.unpack('!HH', wire[current: current + 4])
+        current += 4
+        rdlen -= 4
+        strings = []
+        for i in xrange(3):
+            l = wire[current]
+            current += 1
+            rdlen -= 1
+            if l > rdlen or rdlen < 0:
+                raise dns.exception.FormError
+            s = wire[current: current + l].unwrap()
+            current += l
+            rdlen -= l
+            strings.append(s)
+        (replacement, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                                  current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if origin is not None:
+            replacement = replacement.relativize(origin)
+        return cls(rdclass, rdtype, order, preference, strings[0], strings[1],
+                   strings[2], replacement)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.replacement = self.replacement.choose_relativity(origin,
+                                                              relativize)

+ 60 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NSAP.py

@@ -0,0 +1,60 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import binascii
+
+import dns.exception
+import dns.rdata
+import dns.tokenizer
+
+
+class NSAP(dns.rdata.Rdata):
+
+    """NSAP record.
+
+    @ivar address: a NASP
+    @type address: string
+    @see: RFC 1706"""
+
+    __slots__ = ['address']
+
+    def __init__(self, rdclass, rdtype, address):
+        super(NSAP, self).__init__(rdclass, rdtype)
+        self.address = address
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return "0x%s" % binascii.hexlify(self.address).decode()
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_string()
+        tok.get_eol()
+        if address[0:2] != '0x':
+            raise dns.exception.SyntaxError('string does not start with 0x')
+        address = address[2:].replace('.', '')
+        if len(address) % 2 != 0:
+            raise dns.exception.SyntaxError('hexstring has odd length')
+        address = binascii.unhexlify(address.encode())
+        return cls(rdclass, rdtype, address)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(self.address)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        address = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, address)

+ 23 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/NSAP_PTR.py

@@ -0,0 +1,23 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.nsbase
+
+
+class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS):
+
+    """NSAP-PTR record"""

+ 89 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/PX.py

@@ -0,0 +1,89 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+
+class PX(dns.rdata.Rdata):
+
+    """PX record.
+
+    @ivar preference: the preference value
+    @type preference: int
+    @ivar map822: the map822 name
+    @type map822: dns.name.Name object
+    @ivar mapx400: the mapx400 name
+    @type mapx400: dns.name.Name object
+    @see: RFC 2163"""
+
+    __slots__ = ['preference', 'map822', 'mapx400']
+
+    def __init__(self, rdclass, rdtype, preference, map822, mapx400):
+        super(PX, self).__init__(rdclass, rdtype)
+        self.preference = preference
+        self.map822 = map822
+        self.mapx400 = mapx400
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        map822 = self.map822.choose_relativity(origin, relativize)
+        mapx400 = self.mapx400.choose_relativity(origin, relativize)
+        return '%d %s %s' % (self.preference, map822, mapx400)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        preference = tok.get_uint16()
+        map822 = tok.get_name()
+        map822 = map822.choose_relativity(origin, relativize)
+        mapx400 = tok.get_name(None)
+        mapx400 = mapx400.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    def to_wire(self, file, compress=None, origin=None):
+        pref = struct.pack("!H", self.preference)
+        file.write(pref)
+        self.map822.to_wire(file, None, origin)
+        self.mapx400.to_wire(file, None, origin)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (preference, ) = struct.unpack('!H', wire[current: current + 2])
+        current += 2
+        rdlen -= 2
+        (map822, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                             current)
+        if cused > rdlen:
+            raise dns.exception.FormError
+        current += cused
+        rdlen -= cused
+        if origin is not None:
+            map822 = map822.relativize(origin)
+        (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                              current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if origin is not None:
+            mapx400 = mapx400.relativize(origin)
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.map822 = self.map822.choose_relativity(origin, relativize)
+        self.mapx400 = self.mapx400.choose_relativity(origin, relativize)

+ 83 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/SRV.py

@@ -0,0 +1,83 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+
+import dns.exception
+import dns.rdata
+import dns.name
+
+
+class SRV(dns.rdata.Rdata):
+
+    """SRV record
+
+    @ivar priority: the priority
+    @type priority: int
+    @ivar weight: the weight
+    @type weight: int
+    @ivar port: the port of the service
+    @type port: int
+    @ivar target: the target host
+    @type target: dns.name.Name object
+    @see: RFC 2782"""
+
+    __slots__ = ['priority', 'weight', 'port', 'target']
+
+    def __init__(self, rdclass, rdtype, priority, weight, port, target):
+        super(SRV, self).__init__(rdclass, rdtype)
+        self.priority = priority
+        self.weight = weight
+        self.port = port
+        self.target = target
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        return '%d %d %d %s' % (self.priority, self.weight, self.port,
+                                target)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        priority = tok.get_uint16()
+        weight = tok.get_uint16()
+        port = tok.get_uint16()
+        target = tok.get_name(None)
+        target = target.choose_relativity(origin, relativize)
+        tok.get_eol()
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    def to_wire(self, file, compress=None, origin=None):
+        three_ints = struct.pack("!HHH", self.priority, self.weight, self.port)
+        file.write(three_ints)
+        self.target.to_wire(file, compress, origin)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        (priority, weight, port) = struct.unpack('!HHH',
+                                                 wire[current: current + 6])
+        current += 6
+        rdlen -= 6
+        (target, cused) = dns.name.from_wire(wire[: current + rdlen],
+                                             current)
+        if cused != rdlen:
+            raise dns.exception.FormError
+        if origin is not None:
+            target = target.relativize(origin)
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    def choose_relativity(self, origin=None, relativize=True):
+        self.target = self.target.choose_relativity(origin, relativize)

+ 107 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/WKS.py

@@ -0,0 +1,107 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import socket
+import struct
+
+import dns.ipv4
+import dns.rdata
+from dns._compat import xrange
+
+_proto_tcp = socket.getprotobyname('tcp')
+_proto_udp = socket.getprotobyname('udp')
+
+
+class WKS(dns.rdata.Rdata):
+
+    """WKS record
+
+    @ivar address: the address
+    @type address: string
+    @ivar protocol: the protocol
+    @type protocol: int
+    @ivar bitmap: the bitmap
+    @type bitmap: string
+    @see: RFC 1035"""
+
+    __slots__ = ['address', 'protocol', 'bitmap']
+
+    def __init__(self, rdclass, rdtype, address, protocol, bitmap):
+        super(WKS, self).__init__(rdclass, rdtype)
+        self.address = address
+        self.protocol = protocol
+        if not isinstance(bitmap, bytearray):
+            self.bitmap = bytearray(bitmap)
+        else:
+            self.bitmap = bitmap
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        bits = []
+        for i in xrange(0, len(self.bitmap)):
+            byte = self.bitmap[i]
+            for j in xrange(0, 8):
+                if byte & (0x80 >> j):
+                    bits.append(str(i * 8 + j))
+        text = ' '.join(bits)
+        return '%s %d %s' % (self.address, self.protocol, text)
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        address = tok.get_string()
+        protocol = tok.get_string()
+        if protocol.isdigit():
+            protocol = int(protocol)
+        else:
+            protocol = socket.getprotobyname(protocol)
+        bitmap = bytearray()
+        while 1:
+            token = tok.get().unescape()
+            if token.is_eol_or_eof():
+                break
+            if token.value.isdigit():
+                serv = int(token.value)
+            else:
+                if protocol != _proto_udp and protocol != _proto_tcp:
+                    raise NotImplementedError("protocol must be TCP or UDP")
+                if protocol == _proto_udp:
+                    protocol_text = "udp"
+                else:
+                    protocol_text = "tcp"
+                serv = socket.getservbyname(token.value, protocol_text)
+            i = serv // 8
+            l = len(bitmap)
+            if l < i + 1:
+                for j in xrange(l, i + 1):
+                    bitmap.append(0)
+            bitmap[i] = bitmap[i] | (0x80 >> (serv % 8))
+        bitmap = dns.rdata._truncate_bitmap(bitmap)
+        return cls(rdclass, rdtype, address, protocol, bitmap)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(dns.ipv4.inet_aton(self.address))
+        protocol = struct.pack('!B', self.protocol)
+        file.write(protocol)
+        file.write(self.bitmap)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        address = dns.ipv4.inet_ntoa(wire[current: current + 4])
+        protocol, = struct.unpack('!B', wire[current + 4: current + 5])
+        current += 5
+        rdlen -= 5
+        bitmap = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, address, protocol, bitmap)

+ 33 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/IN/__init__.py

@@ -0,0 +1,33 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Class IN rdata type classes."""
+
+__all__ = [
+    'A',
+    'AAAA',
+    'APL',
+    'DHCID',
+    'IPSECKEY',
+    'KX',
+    'NAPTR',
+    'NSAP',
+    'NSAP_PTR',
+    'PX',
+    'SRV',
+    'WKS',
+]

+ 27 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/__init__.py

@@ -0,0 +1,27 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""DNS rdata type classes"""
+
+__all__ = [
+    'ANY',
+    'IN',
+    'CH',
+    'euibase',
+    'mxbase',
+    'nsbase',
+]

+ 138 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dnskeybase.py

@@ -0,0 +1,138 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import base64
+import struct
+
+import dns.exception
+import dns.dnssec
+import dns.rdata
+
+# wildcard import
+__all__ = ["SEP", "REVOKE", "ZONE",
+           "flags_to_text_set", "flags_from_text_set"]
+
+# flag constants
+SEP = 0x0001
+REVOKE = 0x0080
+ZONE = 0x0100
+
+_flag_by_text = {
+    'SEP': SEP,
+    'REVOKE': REVOKE,
+    'ZONE': ZONE
+}
+
+# We construct the inverse mapping programmatically to ensure that we
+# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
+# would cause the mapping not to be true inverse.
+_flag_by_value = {y: x for x, y in _flag_by_text.items()}
+
+
+def flags_to_text_set(flags):
+    """Convert a DNSKEY flags value to set texts
+    @rtype: set([string])"""
+
+    flags_set = set()
+    mask = 0x1
+    while mask <= 0x8000:
+        if flags & mask:
+            text = _flag_by_value.get(mask)
+            if not text:
+                text = hex(mask)
+            flags_set.add(text)
+        mask <<= 1
+    return flags_set
+
+
+def flags_from_text_set(texts_set):
+    """Convert set of DNSKEY flag mnemonic texts to DNSKEY flag value
+    @rtype: int"""
+
+    flags = 0
+    for text in texts_set:
+        try:
+            flags += _flag_by_text[text]
+        except KeyError:
+            raise NotImplementedError(
+                "DNSKEY flag '%s' is not supported" % text)
+    return flags
+
+
+class DNSKEYBase(dns.rdata.Rdata):
+
+    """Base class for rdata that is like a DNSKEY record
+
+    @ivar flags: the key flags
+    @type flags: int
+    @ivar protocol: the protocol for which this key may be used
+    @type protocol: int
+    @ivar algorithm: the algorithm used for the key
+    @type algorithm: int
+    @ivar key: the public key
+    @type key: string"""
+
+    __slots__ = ['flags', 'protocol', 'algorithm', 'key']
+
+    def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
+        super(DNSKEYBase, self).__init__(rdclass, rdtype)
+        self.flags = flags
+        self.protocol = protocol
+        self.algorithm = algorithm
+        self.key = key
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm,
+                                dns.rdata._base64ify(self.key))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        flags = tok.get_uint16()
+        protocol = tok.get_uint8()
+        algorithm = dns.dnssec.algorithm_from_text(tok.get_string())
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        b64 = b''.join(chunks)
+        key = base64.b64decode(b64)
+        return cls(rdclass, rdtype, flags, protocol, algorithm, key)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
+        file.write(header)
+        file.write(self.key)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        if rdlen < 4:
+            raise dns.exception.FormError
+        header = struct.unpack('!HBB', wire[current: current + 4])
+        current += 4
+        rdlen -= 4
+        key = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], header[1], header[2],
+                   key)
+
+    def flags_to_text_set(self):
+        """Convert a DNSKEY flags value to set texts
+        @rtype: set([string])"""
+        return flags_to_text_set(self.flags)

+ 37 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dnskeybase.pyi

@@ -0,0 +1,37 @@
+from typing import Set, Any
+
+SEP : int
+REVOKE : int
+ZONE : int
+
+def flags_to_text_set(flags : int) -> Set[str]:
+    ...
+
+def flags_from_text_set(texts_set) -> int:
+    ...
+
+from .. import rdata
+
+class DNSKEYBase(rdata.Rdata):
+    def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
+        self.flags : int
+        self.protocol : int
+        self.key : str
+        self.algorithm : int
+
+    def to_text(self, origin : Any = None, relativize=True, **kw : Any):
+        ...
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        ...
+
+    def to_wire(self, file, compress=None, origin=None):
+        ...
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        ...
+
+    def flags_to_text_set(self) -> Set[str]:
+        ...

+ 85 - 0
desktop/core/ext-py3/dnspython-1.16.0/dns/rdtypes/dsbase.py

@@ -0,0 +1,85 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2010, 2011 Nominum, Inc.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+import binascii
+
+import dns.rdata
+import dns.rdatatype
+
+
+class DSBase(dns.rdata.Rdata):
+
+    """Base class for rdata that is like a DS record
+
+    @ivar key_tag: the key tag
+    @type key_tag: int
+    @ivar algorithm: the algorithm
+    @type algorithm: int
+    @ivar digest_type: the digest type
+    @type digest_type: int
+    @ivar digest: the digest
+    @type digest: int
+    @see: draft-ietf-dnsext-delegation-signer-14.txt"""
+
+    __slots__ = ['key_tag', 'algorithm', 'digest_type', 'digest']
+
+    def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type,
+                 digest):
+        super(DSBase, self).__init__(rdclass, rdtype)
+        self.key_tag = key_tag
+        self.algorithm = algorithm
+        self.digest_type = digest_type
+        self.digest = digest
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d %d %s' % (self.key_tag, self.algorithm,
+                                self.digest_type,
+                                dns.rdata._hexify(self.digest,
+                                                  chunksize=128))
+
+    @classmethod
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        key_tag = tok.get_uint16()
+        algorithm = tok.get_uint8()
+        digest_type = tok.get_uint8()
+        chunks = []
+        while 1:
+            t = tok.get().unescape()
+            if t.is_eol_or_eof():
+                break
+            if not t.is_identifier():
+                raise dns.exception.SyntaxError
+            chunks.append(t.value.encode())
+        digest = b''.join(chunks)
+        digest = binascii.unhexlify(digest)
+        return cls(rdclass, rdtype, key_tag, algorithm, digest_type,
+                   digest)
+
+    def to_wire(self, file, compress=None, origin=None):
+        header = struct.pack("!HBB", self.key_tag, self.algorithm,
+                             self.digest_type)
+        file.write(header)
+        file.write(self.digest)
+
+    @classmethod
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        header = struct.unpack("!HBB", wire[current: current + 4])
+        current += 4
+        rdlen -= 4
+        digest = wire[current: current + rdlen].unwrap()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], digest)

Some files were not shown because too many files changed in this diff