跳转到主要内容

用于编码/解码DNS线格式数据包的简单库

项目描述

Dnslib3

一个用于编码/解码DNS线格式数据包的库。

该库提供

  • 支持在线格式、Python对象和Zone/DiG文本表示(dnslib.dns)之间的DNS数据包编码/解码

  • 一个服务器框架,允许简单创建自定义DNS解析器(dnslib.server)以及使用此框架创建的一些示例服务器

  • 一些用于测试的实用工具(dnslib.client、dnslib.proxy、dnslib.intercept)

从版本0.9.0开始添加了对Python 3的支持,这是对该库的重大更新 - 关键变更包括

  • 将'Bimap'接口进行了重大更改,以通过< strong>getitem显式分割正向(value→text)查找,并通过< strong>getattr显式分割反向(text→value)查找。使用旧接口的应用程序需要更新。

  • 默认情况下,现在返回的主机名带有尾随点(符合RFC规范)

  • 大多数对象属性现在与记录定义类型一致,以使生成无效数据包更困难

  • 支持在'Zone'(BIND)文件格式中编码/解码资源记录

  • 支持在'DiG'格式中编码/解码数据包

  • 服务器框架允许通过仅通过扩展DNSResolver类并覆盖'resolve'方法来创建自定义解析器(在大多数情况下)

  • 对错误检测/处理进行了大量修复,这将使库对无效/不支持的数据的鲁棒性大大提高。现在,在解析数据包时,库应返回有效的DNSRecord实例或引发DNSError(通过模糊测试验证)。

  • 改进了实用工具(dnslib.client,dnslib.proxy,dnslib.intercept)。

  • 改进了编码/解码测试,包括在test_decode.py中自动生成测试数据的能力(将输出与DiG进行比较)。

  • 能够比较和差异DNSRecords。

关键DNS数据包处理类位于dnslib.dns中,与标准DNS数据包部分相对应。

  • DNSRecord - DNS数据包的容器。包含:
    • DNSHeader
    • 问题部分,包含零个或多个DNSQuestion对象。
    • 答案部分,包含零个或多个RR对象。
    • 授权部分,包含零个或多个RR对象。
    • 附加部分,包含零个或多个RR对象。
  • DNS RRs(资源记录)包含RR头部和一个RD对象)。
  • 特定的RD类型作为RD的子类实现。
  • DNS标签由DNSLabel类表示 - 在大多数情况下,这处理从/到文本表示的转换,但支持通过字节对象元组表示任意标签。

用法

解码DNS数据包

>>> packet = binascii.unhexlify(b'd5ad818000010005000000000377777706676f6f676c6503636f6d0000010001c00c0005000100000005000803777777016cc010c02c0001000100000005000442f95b68c02c0001000100000005000442f95b63c02c0001000100000005000442f95b67c02c0001000100000005000442f95b93')
>>> d = DNSRecord.parse(packet)
>>> d
<DNS Header: id=0xd5ad type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=5 ns=0 ar=0>
<DNS Question: 'www.google.com.' qtype=A qclass=IN>
<DNS RR: 'www.google.com.' rtype=CNAME rclass=IN ttl=5 rdata='www.l.google.com.'>
<DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.104'>
<DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.99'>
<DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.103'>
<DNS RR: 'www.l.google.com.' rtype=A rclass=IN ttl=5 rdata='66.249.91.147'>

DNSRecord的默认文本表示形式为区域文件格式。

>>> print(d)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54701
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.google.com.                IN      A
;; ANSWER SECTION:
www.google.com.         5       IN      CNAME   www.l.google.com.
www.l.google.com.       5       IN      A       66.249.91.104
www.l.google.com.       5       IN      A       66.249.91.99
www.l.google.com.       5       IN      A       66.249.91.103
www.l.google.com.       5       IN      A       66.249.91.147

创建DNS请求数据包

>>> d = DNSRecord.question("google.com")

(这相当于:d = DNSRecord(q=DNSQuestion("google.com") )

>>> d
<DNS Header: id=... type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
<DNS Question: 'google.com.' qtype=A qclass=IN>

>>> str(DNSRecord.parse(d.pack())) == str(d)
True

>>> print(d)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;google.com.                    IN      A

>>> d = DNSRecord.question("google.com","MX")

(这相当于:d = DNSRecord(q=DNSQuestion("google.com",QTYPE.MX) )

>>> str(DNSRecord.parse(d.pack())) == str(d)
True

>>> print(d)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;google.com.                    IN      MX

创建DNS响应数据包

>>> d = DNSRecord(DNSHeader(qr=1,aa=1,ra=1),
...               q=DNSQuestion("abc.com"),
...               a=RR("abc.com",rdata=A("1.2.3.4")))
>>> d
<DNS Header: id=... type=RESPONSE opcode=QUERY flags=AA,RD,RA rcode='NOERROR' q=1 a=1 ns=0 ar=0>
<DNS Question: 'abc.com.' qtype=A qclass=IN>
<DNS RR: 'abc.com.' rtype=A rclass=IN ttl=0 rdata='1.2.3.4'>
>>> str(DNSRecord.parse(d.pack())) == str(d)
True

>>> print(d)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      A
;; ANSWER SECTION:
abc.com.                0       IN      A       1.2.3.4

也可以从区域文件格式的字符串创建RR。

>>> RR.fromZone("abc.com IN A 1.2.3.4")
[<DNS RR: 'abc.com.' rtype=A rclass=IN ttl=0 rdata='1.2.3.4'>]

(Note: this produces a list of RRs which should be unpacked if being
passed to add_answer/add_auth/add_ar etc)

>>> q = DNSRecord.question("abc.com")
>>> a = q.reply()
>>> a.add_answer(*RR.fromZone("abc.com 60 A 1.2.3.4"))
>>> print(a)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      A
;; ANSWER SECTION:
abc.com.                60      IN      A       1.2.3.4

区域文件可以包含多个条目,并支持RFC1035中定义的大多数正常格式(特别是不包括$INCLUDE)。

>>> z = '''
...         $TTL 300
...         $ORIGIN abc.com
...
...         @       IN      MX      10  mail.abc.com.
...         www     IN      A       1.2.3.4
...                 IN      TXT     "Some Text"
...         mail    IN      CNAME   www.abc.com.
... '''
>>> for rr in RR.fromZone(textwrap.dedent(z)):
...     print(rr)
abc.com.                300     IN      MX      10 mail.abc.com.
www.abc.com.            300     IN      A       1.2.3.4
www.abc.com.            300     IN      TXT     "Some Text"
mail.abc.com.           300     IN      CNAME   www.abc.com.

创建对DNS查询的骨架响应

>>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY))
>>> a = q.reply()
>>> a.add_answer(RR("abc.com",QTYPE.A,rdata=A("1.2.3.4"),ttl=60))
>>> str(DNSRecord.parse(a.pack())) == str(a)
True
>>> print(a)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      ANY
;; ANSWER SECTION:
abc.com.                60      IN      A       1.2.3.4

添加额外的RR

>>> a.add_answer(RR("xxx.abc.com",QTYPE.A,rdata=A("1.2.3.4")))
>>> a.add_answer(RR("xxx.abc.com",QTYPE.AAAA,rdata=AAAA("1234:5678::1")))
>>> str(DNSRecord.parse(a.pack())) == str(a)
True
>>> print(a)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      ANY
;; ANSWER SECTION:
abc.com.                60      IN      A       1.2.3.4
xxx.abc.com.            0       IN      A       1.2.3.4
xxx.abc.com.            0       IN      AAAA    1234:5678::1

也可以从区域文件格式的字符串创建响应

>>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY))
>>> a = q.replyZone("abc.com 60 IN CNAME xxx.abc.com")
>>> print(a)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      ANY
;; ANSWER SECTION:
abc.com.                60      IN      CNAME   xxx.abc.com.

>>> str(DNSRecord.parse(a.pack())) == str(a)
True

>>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY))
>>> a = q.replyZone(textwrap.dedent(z))
>>> print(a)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;abc.com.                       IN      ANY
;; ANSWER SECTION:
abc.com.                300     IN      MX      10 mail.abc.com.
www.abc.com.            300     IN      A       1.2.3.4
www.abc.com.            300     IN      TXT     "Some Text"
mail.abc.com.           300     IN      CNAME   www.abc.com.

发送DNSSEC请求(包含DO标志的EDNS OPT记录和AD头部标志)

>>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.A))
>>> q.add_ar(EDNS0(flags="do",udp_len=4096))
>>> q.header.ad = 1
>>> print(q)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; QUESTION SECTION:
;abc.com.                       IN      A
;; ADDITIONAL SECTION:
;; OPT PSEUDOSECTION
; EDNS: version: 0, flags: do; udp: 4096

注意,在使用库时,您应该始终验证接收到的TXID

q = DNSRecord.question("abc.com")
a_pkt = q.send(address,port,tcp=args.tcp)
a = DNSRecord.parse(a_pkt)
if q.header.id != a.header.id:
    raise DNSError('Response transaction id does not match query transaction id')

库还包括一个简单的框架,用于在dnslib.server中生成自定义DNS解析器(请参阅模块文档)。在大多数情况下,只需实现一个自定义的'resolve'方法,该方法接收一个问题对象并返回一个响应。

提供了一些示例解析器作为示例(请参阅CLI --help)

  • dnslib.fixedresolver - 对所有请求返回固定响应
  • dnslib.zoneresolver - 从区域文件响应
  • dnslib.shellresolver - 调用shell脚本来生成响应

该库包括一些客户端实用工具

  • 类似DiG的客户端库

      # python -m dnslib.client --help
    
  • DNS代理服务器

      # python -m dnslib.proxy --help
    
  • 拦截DNS代理服务器(替换指定域的代理响应)

      # python -m dnslib.intercept --help
    

变更日志

 *   0.1     2010-09-19  Initial Release
 *   0.2     2010-09-22  Minor fixes
 *   0.3     2010-10-02  Add DNSLabel class to support arbitrary labels (embedded '.')
 *   0.4     2012-02-26  Merge with dbslib-circuits
 *   0.5     2012-09-13  Add support for RFC2136 DDNS updates
                         Patch provided by Wesley Shields <wxs@FreeBSD.org> - thanks
 *   0.6     2012-10-20  Basic AAAA support
 *   0.7     2012-10-20  Add initial EDNS0 support (untested)
 *   0.8     2012-11-04  Add support for NAPTR, Authority RR and additional RR
                         Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks
 *   0.8.1   2012-11-05  Added NAPTR test case and fixed logic error
                         Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks
 *   0.8.2   2012-11-11  Patch to fix IPv6 formatting
                         Patch provided by Torbjorn Lonnemark (https://bitbucket.org/tobbezz) - thanks
 *   0.8.3   2013-04-27  Don't parse rdata if rdlength is 0
                         Patch provided by Wesley Shields <wxs@FreeBSD.org> - thanks
 *   0.9.0   2014-05-05  Major update including Py3 support (see docs)
 *   0.9.1   2014-05-05  Minor fixes
 *   0.9.2   2014-08-26  Fix Bimap handling of unknown mappings to avoid exception in printing
                         Add typed attributes to classes
                         Misc fixes from James Mills - thanks
 *   0.9.3   2014-08-26  Workaround for argparse bug which raises AssertionError if [] is
                         present in option text (really?)
 *   0.9.4   2015-04-10  Fix to support multiple strings in TXT record
                         Patch provided by James Cherry (https://bitbucket.org/james_cherry) - thanks
                         NOTE: For consistency this patch changes the 'repr' output for
                               TXT records to always be quoted
 *   0.9.5   2015-10-27  Add threading & timeout handling to DNSServer
 *   0.9.6   2015-10-28  Replace strftime in RRSIG formatting to avoid possible locale issues
                         Identified by Bryan Everly - thanks
 *   0.9.7   2017-01-15  Sort out CAA/TYPE257 DiG parsing mismatch
 *   0.9.8   2019-02-25  Force DNSKEY key to be bytes object
                         Catch Bimap __wrapped__ attr (used by inspect module in 3.7)
 *   0.9.9   2019-03-19  Add support for DNSSEC flag getters/setters (from <raul@dinosec.com> - thanks)
                         Added --dnssec flags to dnslib.client & dnslib.test_decode (sets EDNS0 DO flag)
                         Added EDNS0 support to dnslib.digparser
 *   0.9.10  2019-03-24  Fixes to DNSSEC support
                         Add NSEC RR support
                         Add --dnssec flag to dnslib.client & dnslib.test_decode
                         Quote/unquote non-printable characters in DNS labels
                         Update test data
                         (Thanks to <raul@dinosec.com> for help)
 *   0.9.11  2019-12-17  Encode NOTIFY Opcode (Issue #26)
 *   0.9.12  2019-12-17  Transition master repository to Github (Bitbucket shutting down hg)
 *   0.9.13  2020-06-01  Handle truncated requests in server.py (Issue #9)
                         Replace thred.isAlive with thread.is_alive (Deprecated in Py3.9)
                         Merged Pull Request #4 (Extra options for intercept.py) - thanks to @nolanl
 *   0.9.14  2020-06-09  Merged Pull Request #10 (Return doctest status via exit code)
                         Thanks to @mgorny
 *   0.9.15  2021-05-07  DNSServer fixes - support IPv6 (from Pull Request #21) - thanks to @mikma
                                         - deamon threads (Pull Request #19) - thanks to @wojons
                         Add unsupported RR types (Issue #27)
 *   0.9.16  2021-05-07  Merge pull request #23 from Tugzrida/patch-1
                            Add support for all RR types to NSEC type bitmap
                         Merge pull request #17 from sunds/issue_16
                            Issue 16: uncaught exceptions leak open sockets
 *   0.9.18  2022-01-09  Validate TXID in client.py (Issue #30 - thanks to @daniel4x)
 *   0.9.19  2022-01-09  Allow custom log function (logf) in  DNSLogger
                            (Issue #31 - thanks to @DmitryFrolovTri)
 *   0.9.20  2022-07-17  Fix DeprecationWarnings about invalid escape sequences
                            (Pull-Request #39 - thanks to @brianmaissy)
                         Make DNSLabel matchSuffix and stripSuffix case-insensitive
                            (Pull-Request #37 - thanks to @NiKiZe)
                         Add support for HTTPS RR
                            (Pull-Request #35 - thanks to @jkl-caliber)
                         Fix display of non-printable characters in TXT records
                            (Issue #32 - thanks to @sbv-csis)
                         Add --strip-aaaa option to dnslib.proxy
 *   0.9.21  2022-09-19  Minor clean-up / add wheels to distro
 *   0.9.22  2022-09027  Issue #43 (0.9.21 Raises TypeError instead of DNSError when failing to parse HTTPS records)
                         Note that we just fix the exception - there still seems to be a problem with parsing HTTPS records
                         (Thanks to @robinlandstrom)
 *   0.9.23  2022-10-28  Issue #43: HTTPS reads after RD end (thanks to @robinlandstrom for pull request)
                         Issue #45: Dnslib fails to handle unknown RR types in NSEC RD type bitmap
                            Bimap now supports a function to map unknown types which we use to
                            dynamically map from rtype <-> TYPExxxx for unknown record types
                            RR zone representation updated to match RFC3597
                         Pull Request #47: Add support for DS, SSHFP, and TLSA records (thanks to @rmbolger)
 *   0.9.24  2024-01-02  Merge multiple PRs
                            #49 - Generate README.md (via symlink)
                            #51 - Update Github CI checkout & setup-python actions (and remove Python 2.7 CI support)
                                  (thanks to @SpencerIsGiddy)
                            #54 - Support for RP records (thanks to @ryan-gang)
                            #57 - Support for LOC records (thanks to @valentinesd)
                         (Note that this will be the last release supporting Python 2.7 and Python <3.7)

许可证

BSD

作者

  • PaulC

主存储库/问题

(注意: https://bitbucket.org/paulc/dnslib 已弃用,将不再更新)

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解更多关于 安装包 的信息。

源分布

dnslib3-0.10.0.tar.gz (79.1 kB 查看哈希值)

上传时间

构建分布

dnslib3-0.10.0-py3-none-any.whl (115.3 kB 查看哈希值)

上传时间 Python 3

支持