├── README.md ├── conf ├── config.json └── hosts_repository_config.json ├── data ├── rpz.json └── wrcd.json ├── dnslib ├── __init__.py ├── __init__.pyc ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── bimap.cpython-36.pyc │ ├── bit.cpython-36.pyc │ ├── buffer.cpython-36.pyc │ ├── digparser.cpython-36.pyc │ ├── dns.cpython-36.pyc │ ├── label.cpython-36.pyc │ ├── lex.cpython-36.pyc │ └── ranges.cpython-36.pyc ├── bimap.py ├── bimap.pyc ├── bit.py ├── bit.pyc ├── buffer.py ├── buffer.pyc ├── client.py ├── digparser.py ├── digparser.pyc ├── dns.py ├── dns.pyc ├── fixedresolver.py ├── intercept.py ├── label.py ├── label.pyc ├── lex.py ├── lex.pyc ├── proxy.py ├── ranges.py ├── ranges.pyc ├── server.py ├── shellresolver.py ├── test │ ├── 20120113._domainkey.gmail.com.-TXT │ ├── _sip._udp.sipgate.co.uk-SRV │ ├── dig │ │ ├── google.com-A.dig │ │ └── google.com-ANY.dig │ ├── e164.org-NAPTR │ ├── example.com-ANY │ ├── facebook.com-AAAA │ ├── google.com-A │ ├── google.com-AAAA │ ├── google.com-ANY │ ├── google.com-MX │ ├── google.com-SOA │ ├── google.com-TXT │ ├── iana.org-ANY │ ├── in-addr.arpa-PTR │ ├── microsoft.com-ANY │ ├── sip2sip.info-ANY │ ├── sip2sip.info-NAPTR │ └── sipgate.co.uk-ANY ├── test_decode.py └── zoneresolver.py ├── start.py └── update.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | # dowsDNS 3 | 4 | 快速翻越中国防火墙 ----- 仅支持Linux/Windows/Mac 5 | 6 | # Linux ----- 已测试 ubuntu 16.04 python 测试版本 3.5 7 | ## 本机使用 8 | ### 更改 DNS 域名服务器 9 | 1. 将 conf/config.json 中的 Local_dns_server 的值改为电脑的 127.0.0.1 10 | 2. 在 /etc/resolvconf/resolv.conf.d/base 里添加 nameserver 127.0.0.1 11 | 3. sudo resolvconf -u 12 | 4. sudo systemctl restart network-manager.service 13 | 14 | ### 启动 15 | * sudo ./start.py 16 | 17 | ### 更新 18 | * ./update.py 19 | 20 | ## 局域网共享使用 21 | 1. 将 conf/config.json 中的 Local_dns_server 的值改为电脑的 ip 22 | 2. firewall-cmd –add-port=53/udp     –permanent 23 | 3. sudo ./start.py 24 | 4. 同一局域网下,把其他设备 DNS 改为 运行程序的电脑本地 IP 即可 25 | 26 | ## 通用方法 27 | 1. 将 conf/config.json 中的 Local_dns_server 的值改为电脑的 0.0.0.0 28 | 2. firewall-cmd –add-port=53/udp     –permanent 29 | 3. 在 /etc/NetworkManager/NetworkManager.conf 中的 dns=dnsmasq 前面加 # 30 | 4. 在 /etc/resolvconf/resolv.conf.d/base 里添加 nameserver 127.0.0.1 31 | 5. sudo resolvconf -u 32 | 6. sudo systemctl restart network-manager.service 33 | 7. 重启电脑 34 | 8. 同一局域网下,把其他设备或本机的 DNS 改为 运行程序的电脑本地 IP 即可 35 | 36 | # Mac ---- 未测试 --- 欢迎有测试的同学提意见 37 | 1. sudo ./start.py 38 | 2. 将DNS服务器改为 127.0.0.1 39 | 3. 重启网络服务和浏览器即可 40 | 41 | * 更新hosts:./update.py 42 | 43 | # Windows ---- 已测试 --- xp -- python 3.4 44 | ## 启动 45 | 1. 将命令行切换到当前目录 46 | 2. python start.py 47 | 48 | ## 更新 49 | * python update.py 50 | 51 | # 数据引用 52 | * https://pypi.python.org/pypi/dnslib 53 | * https://github.com/racaljk/hosts 54 | 55 | 56 | # 更多请参阅 57 | * http://www.codedev.cn/forum.php?mod=viewthread&tid=10 58 | -------------------------------------------------------------------------------- /conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Remote_dns_server" : "114.114.114.114", 3 | "Remote_dns_port":53, 4 | "Rpz_json_path":"./data/rpz.json", 5 | "Local_dns_server" : "0.0.0.0", 6 | "Local_dns_port" : 53, 7 | "sni_proxy_on":false, 8 | "sni_proxy_ip":"219.76.4.3" 9 | } 10 | -------------------------------------------------------------------------------- /conf/hosts_repository_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts":{ 3 | "racaljk":"https://github.com/racaljk/hosts/raw/master/hosts" 4 | }, 5 | "wrcd":"https://raw.githubusercontent.com/liuyunbin/dowsDNS/master/data/wrcd.json" 6 | } 7 | -------------------------------------------------------------------------------- /data/wrcd.json: -------------------------------------------------------------------------------- 1 | { 2 | "gvt0.com":"219.76.4.4", 3 | "*.gvt0.com":"219.76.4.4", 4 | "gvt1.com":"219.76.4.4", 5 | "*.gvt1.com":"219.76.4.4", 6 | "gvt2.com":"219.76.4.4", 7 | "*.gvt2.com":"219.76.4.4", 8 | "gvt3.com":"219.76.4.4", 9 | "*.gvt3.com":"219.76.4.4", 10 | "youtube.com":"219.76.4.4", 11 | "*.youtube.com":"219.76.4.4", 12 | "ytimg.com":"219.76.4.4", 13 | "*.ytimg.com":"219.76.4.4", 14 | "youtu.be":"219.76.4.4", 15 | "*.youtu.be":"219.76.4.4", 16 | "googlevideo.com":"219.76.4.4", 17 | "*.googlevideo.com":"219.76.4.4", 18 | "ggpht.com":"219.76.4.4", 19 | "*.ggpht.com":"219.76.4.4" 20 | } 21 | -------------------------------------------------------------------------------- /dnslib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | dnslib 5 | ------ 6 | 7 | A library to encode/decode DNS wire-format packets supporting both 8 | Python 2.7 and Python 3.2+. 9 | 10 | The library provides: 11 | 12 | * Support for encoding/decoding DNS packets between wire format, 13 | python objects, and Zone/DiG textual representation (dnslib.dns) 14 | 15 | * A server framework allowing the simple creation of custom DNS 16 | resolvers (dnslib.server) and a number of example servers 17 | created using this framework 18 | 19 | * A number of utilities for testing (dnslib.client, dnslib.proxy, 20 | dnslib.intercept) 21 | 22 | Python 3 support was added in Version 0.9.0 which represented a fairly 23 | major update to the library - the key changes include: 24 | 25 | * Python 2.7/3.2+ support (the last version supporting Python 2.6 26 | or earlier was version 0.8.3) 27 | 28 | * The 'Bimap' interface was changed significantly to explicitly 29 | split forward (value->text) lookups via __getitem__ and 30 | reverse (text->value) lookups via __getattr__. Applications 31 | using the old interface will need to be updated. 32 | 33 | * Hostnames are now returned with a trailing dot by default (in 34 | line with RFC) 35 | 36 | * Most object attributes are now typed in line with the record 37 | definitions to make it harder to generate invalid packets 38 | 39 | * Support for encoding/decoding resource records in 'Zone' (BIND) 40 | file format 41 | 42 | * Support for encoding/decoding packets in 'DiG' format 43 | 44 | * Server framework allowing (in most cases) custom resolvers to 45 | be created by just subclassing the DNSResolver class and 46 | overriding the 'resolve' method 47 | 48 | * A lot of fixes to error detection/handling which should make 49 | the library much more robust to invalid/unsupported data. The 50 | library should now either return a valid DNSRecord instance 51 | when parsing a packet or raise DNSError (tested via fuzzing) 52 | 53 | * Improved utilities (dnslib.client, dnslib.proxy, dnslib.intercept) 54 | 55 | * Improvements to encoding/decoding tests including the ability 56 | to generate test data automatically in test_decode.py (comparing 57 | outputs against DiG) 58 | 59 | * Ability to compare and diff DNSRecords 60 | 61 | Classes 62 | ------- 63 | 64 | The key DNS packet handling classes are in dnslib.dns and map to the 65 | standard DNS packet sections: 66 | 67 | * DNSRecord - container for DNS packet. Contains: 68 | - DNSHeader 69 | - Question section containing zero or more DNSQuestion objects 70 | - Answer section containing zero or more RR objects 71 | - Authority section containing zero or more RR objects 72 | - Additional section containing zero or more RR objects 73 | * DNS RRs (resource records) contain an RR header and an RD object) 74 | * Specific RD types are implemented as subclasses of RD 75 | * DNS labels are represented by a DNSLabel class - in most cases 76 | this handles conversion to/from textual representation however 77 | does support arbitatry labels via a tuple of bytes objects 78 | 79 | Usage 80 | ----- 81 | 82 | To decode a DNS packet: 83 | 84 | >>> packet = binascii.unhexlify(b'd5ad818000010005000000000377777706676f6f676c6503636f6d0000010001c00c0005000100000005000803777777016cc010c02c0001000100000005000442f95b68c02c0001000100000005000442f95b63c02c0001000100000005000442f95b67c02c0001000100000005000442f95b93') 85 | >>> d = DNSRecord.parse(packet) 86 | >>> d 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | The default text representation of the DNSRecord is in zone file format: 96 | 97 | >>> print(d) 98 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54701 99 | ;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 0 100 | ;; QUESTION SECTION: 101 | ;www.google.com. IN A 102 | ;; ANSWER SECTION: 103 | www.google.com. 5 IN CNAME www.l.google.com. 104 | www.l.google.com. 5 IN A 66.249.91.104 105 | www.l.google.com. 5 IN A 66.249.91.99 106 | www.l.google.com. 5 IN A 66.249.91.103 107 | www.l.google.com. 5 IN A 66.249.91.147 108 | 109 | To create a DNS Request Packet: 110 | 111 | >>> d = DNSRecord.question("google.com") 112 | 113 | (This is equivalent to: d = DNSRecord(q=DNSQuestion("google.com") ) 114 | 115 | >>> d 116 | 117 | 118 | 119 | >>> str(DNSRecord.parse(d.pack())) == str(d) 120 | True 121 | 122 | >>> print(d) 123 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 124 | ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 125 | ;; QUESTION SECTION: 126 | ;google.com. IN A 127 | 128 | >>> d = DNSRecord.question("google.com","MX") 129 | 130 | (This is equivalent to: d = DNSRecord(q=DNSQuestion("google.com",QTYPE.MX) ) 131 | 132 | >>> str(DNSRecord.parse(d.pack())) == str(d) 133 | True 134 | 135 | >>> print(d) 136 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 137 | ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 138 | ;; QUESTION SECTION: 139 | ;google.com. IN MX 140 | 141 | To create a DNS Response Packet: 142 | 143 | >>> d = DNSRecord(DNSHeader(qr=1,aa=1,ra=1), 144 | ... q=DNSQuestion("abc.com"), 145 | ... a=RR("abc.com",rdata=A("1.2.3.4"))) 146 | >>> d 147 | 148 | 149 | 150 | >>> str(DNSRecord.parse(d.pack())) == str(d) 151 | True 152 | 153 | >>> print(d) 154 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 155 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 156 | ;; QUESTION SECTION: 157 | ;abc.com. IN A 158 | ;; ANSWER SECTION: 159 | abc.com. 0 IN A 1.2.3.4 160 | 161 | It is also possible to create RRs from a string in zone file format 162 | 163 | >>> RR.fromZone("abc.com IN A 1.2.3.4") 164 | [] 165 | 166 | (Note: this produces a list of RRs which should be unpacked if being 167 | passed to add_answer/add_auth/add_ar etc) 168 | 169 | >>> q = DNSRecord.question("abc.com") 170 | >>> a = q.reply() 171 | >>> a.add_answer(*RR.fromZone("abc.com 60 A 1.2.3.4")) 172 | >>> print(a) 173 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 174 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 175 | ;; QUESTION SECTION: 176 | ;abc.com. IN A 177 | ;; ANSWER SECTION: 178 | abc.com. 60 IN A 1.2.3.4 179 | 180 | The zone file can contain multiple entries and supports most of the normal 181 | format defined in RFC1035 (specifically not $INCLUDE) 182 | 183 | >>> z = ''' 184 | ... $TTL 300 185 | ... $ORIGIN abc.com 186 | ... 187 | ... @ IN MX 10 mail.abc.com. 188 | ... www IN A 1.2.3.4 189 | ... IN TXT "Some Text" 190 | ... mail IN CNAME www.abc.com. 191 | ... ''' 192 | >>> for rr in RR.fromZone(textwrap.dedent(z)): 193 | ... print(rr) 194 | abc.com. 300 IN MX 10 mail.abc.com. 195 | www.abc.com. 300 IN A 1.2.3.4 196 | www.abc.com. 300 IN TXT "Some Text" 197 | mail.abc.com. 300 IN CNAME www.abc.com. 198 | 199 | To create a skeleton reply to a DNS query: 200 | 201 | >>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY)) 202 | >>> a = q.reply() 203 | >>> a.add_answer(RR("abc.com",QTYPE.A,rdata=A("1.2.3.4"),ttl=60)) 204 | >>> str(DNSRecord.parse(a.pack())) == str(a) 205 | True 206 | >>> print(a) 207 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 208 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 209 | ;; QUESTION SECTION: 210 | ;abc.com. IN ANY 211 | ;; ANSWER SECTION: 212 | abc.com. 60 IN A 1.2.3.4 213 | 214 | Add additional RRs: 215 | 216 | >>> a.add_answer(RR("xxx.abc.com",QTYPE.A,rdata=A("1.2.3.4"))) 217 | >>> a.add_answer(RR("xxx.abc.com",QTYPE.AAAA,rdata=AAAA("1234:5678::1"))) 218 | >>> str(DNSRecord.parse(a.pack())) == str(a) 219 | True 220 | >>> print(a) 221 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 222 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0 223 | ;; QUESTION SECTION: 224 | ;abc.com. IN ANY 225 | ;; ANSWER SECTION: 226 | abc.com. 60 IN A 1.2.3.4 227 | xxx.abc.com. 0 IN A 1.2.3.4 228 | xxx.abc.com. 0 IN AAAA 1234:5678::1 229 | 230 | 231 | It is also possible to create a reply from a string in zone file format: 232 | 233 | >>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY)) 234 | >>> a = q.replyZone("abc.com 60 IN CNAME xxx.abc.com") 235 | >>> print(a) 236 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 237 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 238 | ;; QUESTION SECTION: 239 | ;abc.com. IN ANY 240 | ;; ANSWER SECTION: 241 | abc.com. 60 IN CNAME xxx.abc.com. 242 | 243 | >>> str(DNSRecord.parse(a.pack())) == str(a) 244 | True 245 | 246 | >>> q = DNSRecord(q=DNSQuestion("abc.com",QTYPE.ANY)) 247 | >>> a = q.replyZone(textwrap.dedent(z)) 248 | >>> print(a) 249 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ... 250 | ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 0 251 | ;; QUESTION SECTION: 252 | ;abc.com. IN ANY 253 | ;; ANSWER SECTION: 254 | abc.com. 300 IN MX 10 mail.abc.com. 255 | www.abc.com. 300 IN A 1.2.3.4 256 | www.abc.com. 300 IN TXT "Some Text" 257 | mail.abc.com. 300 IN CNAME www.abc.com. 258 | 259 | 260 | The library also includes a simple framework for generating custom DNS 261 | resolvers in dnslib.server (see module docs). In post cases this just 262 | requires implementing a custom 'resolve' method which receives a question 263 | object and returns a response. 264 | 265 | A number of sample resolvers are provided as examples (see CLI --help): 266 | 267 | * dnslib.fixedresolver - Respond to all requests with fixed response 268 | * dnslib.zoneresolver - Respond from Zone file 269 | * dnslib.shellresolver - Call shell script to generate response 270 | 271 | The library includes a number of client utilities: 272 | 273 | * DiG like client library 274 | 275 | # python -m dnslib.client --help 276 | 277 | * DNS Proxy Server 278 | 279 | # python -m dnslib.proxy --help 280 | 281 | * Intercepting DNS Proxy Server (replace proxy responses for specified domains) 282 | 283 | # python -m dnslib.intercept --help 284 | 285 | 286 | Changelog: 287 | ---------- 288 | 289 | * 0.1 2010-09-19 Initial Release 290 | * 0.2 2010-09-22 Minor fixes 291 | * 0.3 2010-10-02 Add DNSLabel class to support arbitrary labels (embedded '.') 292 | * 0.4 2012-02-26 Merge with dbslib-circuits 293 | * 0.5 2012-09-13 Add support for RFC2136 DDNS updates 294 | Patch provided by Wesley Shields - thanks 295 | * 0.6 2012-10-20 Basic AAAA support 296 | * 0.7 2012-10-20 Add initial EDNS0 support (untested) 297 | * 0.8 2012-11-04 Add support for NAPTR, Authority RR and additional RR 298 | Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks 299 | * 0.8.1 2012-11-05 Added NAPTR test case and fixed logic error 300 | Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks 301 | * 0.8.2 2012-11-11 Patch to fix IPv6 formatting 302 | Patch provided by Torbjörn Lönnemark (https://bitbucket.org/tobbezz) - thanks 303 | * 0.8.3 2013-04-27 Don't parse rdata if rdlength is 0 304 | Patch provided by Wesley Shields - thanks 305 | * 0.9.0 2014-05-05 Major update including Py3 support (see docs) 306 | * 0.9.1 2014-05-05 Minor fixes 307 | * 0.9.2 2014-08-26 Fix Bimap handling of unknown mappings to avoid exception in printing 308 | Add typed attributes to classes 309 | Misc fixes from James Mills - thanks 310 | * 0.9.3 2014-08-26 Workaround for argparse bug which raises AssertionError if [] is 311 | present in option text (really?) 312 | * 0.9.4 2015-04-10 Fix to support multiple strings in TXT record 313 | Patch provided by James Cherry (https://bitbucket.org/james_cherry) - thanks 314 | NOTE: For consistency this patch changes the 'repr' output for 315 | TXT records to always be quoted 316 | * 0.9.5 2015-10-27 Add threading & timeout handling to DNSServer 317 | * 0.9.6 2015-10-28 Replace strftime in RRSIG formatting to avoid possible locale issues 318 | Identified by Bryan Everly - thanks 319 | * 0.9.7 2017-01-15 Sort out CAA/TYPE257 DiG parsing mismatch 320 | 321 | 322 | License: 323 | -------- 324 | 325 | BSD 326 | 327 | Author: 328 | ------- 329 | 330 | * Paul Chakravarti (paul.chakravarti@gmail.com) 331 | 332 | Master Repository/Issues: 333 | ------------------------- 334 | 335 | * https://bitbucket.org/paulc/dnslib 336 | (Cloned on GitHub: https://github.com/paulchakravarti/dnslib) 337 | 338 | """ 339 | 340 | from dnslib.dns import * 341 | 342 | version = "0.9.7" 343 | 344 | if __name__ == '__main__': 345 | import doctest,textwrap 346 | doctest.testmod(optionflags=doctest.ELLIPSIS) 347 | 348 | -------------------------------------------------------------------------------- /dnslib/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__init__.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/bimap.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/bimap.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/bit.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/bit.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/buffer.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/buffer.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/digparser.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/digparser.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/dns.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/dns.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/label.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/label.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/lex.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/lex.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/__pycache__/ranges.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/__pycache__/ranges.cpython-36.pyc -------------------------------------------------------------------------------- /dnslib/bimap.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Bimap - bidirectional mapping between code/value 5 | """ 6 | 7 | class BimapError(Exception): 8 | pass 9 | 10 | class Bimap(object): 11 | 12 | """ 13 | Bi-directional mapping between code/text. 14 | 15 | Initialised using: 16 | 17 | name: Used for exceptions 18 | dict: Dict mapping from code (numeric) to text 19 | error: Error type to raise if key not found 20 | 21 | The class provides: 22 | 23 | * A 'forward' map (code->text) which is accessed through 24 | __getitem__ (bimap[code]) 25 | * A 'reverse' map (code>value) which is accessed through 26 | __getattr__ (bimap.text) 27 | * A 'get' method which does a forward lookup (code->text) 28 | and returns a textual version of code if there is no 29 | explicit mapping (or default provided) 30 | 31 | >>> class TestError(Exception): 32 | ... pass 33 | 34 | >>> TEST = Bimap('TEST',{1:'A', 2:'B', 3:'C'},TestError) 35 | >>> TEST[1] 36 | 'A' 37 | >>> TEST.A 38 | 1 39 | >>> TEST.X 40 | Traceback (most recent call last): 41 | ... 42 | TestError: TEST: Invalid reverse lookup: [X] 43 | >>> TEST[99] 44 | Traceback (most recent call last): 45 | ... 46 | TestError: TEST: Invalid forward lookup: [99] 47 | >>> TEST.get(99) 48 | '99' 49 | 50 | """ 51 | 52 | def __init__(self,name,forward,error=KeyError): 53 | self.name = name 54 | self.error = error 55 | self.forward = forward.copy() 56 | self.reverse = dict([(v,k) for (k,v) in list(forward.items())]) 57 | 58 | def get(self,k,default=None): 59 | try: 60 | return self.forward[k] 61 | except KeyError as e: 62 | return default or str(k) 63 | 64 | def __getitem__(self,k): 65 | try: 66 | return self.forward[k] 67 | except KeyError as e: 68 | raise self.error("%s: Invalid forward lookup: [%s]" % (self.name,k)) 69 | 70 | def __getattr__(self,k): 71 | try: 72 | return self.reverse[k] 73 | except KeyError as e: 74 | raise self.error("%s: Invalid reverse lookup: [%s]" % (self.name,k)) 75 | 76 | if __name__ == '__main__': 77 | import doctest 78 | doctest.testmod() 79 | -------------------------------------------------------------------------------- /dnslib/bimap.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/bimap.pyc -------------------------------------------------------------------------------- /dnslib/bit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Some basic bit mainpulation utilities 5 | """ 6 | from __future__ import print_function 7 | 8 | FILTER = bytearray([ (i < 32 or i > 127) and 46 or i for i in range(256) ]) 9 | 10 | def hexdump(src, length=16, prefix=''): 11 | """ 12 | Print hexdump of string 13 | 14 | >>> print(hexdump(b"abcd" * 4)) 15 | 0000 61 62 63 64 61 62 63 64 61 62 63 64 61 62 63 64 abcdabcd abcdabcd 16 | 17 | >>> print(hexdump(bytearray(range(48)))) 18 | 0000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ........ ........ 19 | 0010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ........ ........ 20 | 0020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&' ()*+,-./ 21 | 22 | """ 23 | n = 0 24 | left = length // 2 25 | right = length - left 26 | result= [] 27 | src = bytearray(src) 28 | while src: 29 | s,src = src[:length],src[length:] 30 | l,r = s[:left],s[left:] 31 | hexa = "%-*s" % (left*3,' '.join(["%02x"%x for x in l])) 32 | hexb = "%-*s" % (right*3,' '.join(["%02x"%x for x in r])) 33 | lf = l.translate(FILTER) 34 | rf = r.translate(FILTER) 35 | result.append("%s%04x %s %s %s %s" % (prefix, n, hexa, hexb, 36 | lf.decode(), rf.decode())) 37 | n += length 38 | return "\n".join(result) 39 | 40 | def get_bits(data,offset,bits=1): 41 | """ 42 | Get specified bits from integer 43 | 44 | >>> bin(get_bits(0b0011100,2)) 45 | '0b1' 46 | >>> bin(get_bits(0b0011100,0,4)) 47 | '0b1100' 48 | 49 | """ 50 | mask = ((1 << bits) - 1) << offset 51 | return (data & mask) >> offset 52 | 53 | def set_bits(data,value,offset,bits=1): 54 | """ 55 | Set specified bits in integer 56 | 57 | >>> bin(set_bits(0,0b1010,0,4)) 58 | '0b1010' 59 | >>> bin(set_bits(0,0b1010,3,4)) 60 | '0b1010000' 61 | """ 62 | mask = ((1 << bits) - 1) << offset 63 | clear = 0xffff ^ mask 64 | data = (data & clear) | ((value << offset) & mask) 65 | return data 66 | 67 | def binary(n,count=16,reverse=False): 68 | """ 69 | Display n in binary (only difference from built-in `bin` is 70 | that this function returns a fixed width string and can 71 | optionally be reversed 72 | 73 | >>> binary(6789) 74 | '0001101010000101' 75 | >>> binary(6789,8) 76 | '10000101' 77 | >>> binary(6789,reverse=True) 78 | '1010000101011000' 79 | 80 | """ 81 | bits = [str((n >> y) & 1) for y in range(count-1, -1, -1)] 82 | if reverse: 83 | bits.reverse() 84 | return "".join(bits) 85 | 86 | if __name__ == '__main__': 87 | import doctest 88 | doctest.testmod() 89 | 90 | -------------------------------------------------------------------------------- /dnslib/bit.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/bit.pyc -------------------------------------------------------------------------------- /dnslib/buffer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Buffer - simple data buffer 5 | """ 6 | 7 | import binascii,struct 8 | 9 | class BufferError(Exception): 10 | pass 11 | 12 | class Buffer(object): 13 | 14 | """ 15 | A simple data buffer - supports packing/unpacking in struct format 16 | 17 | # Needed for Python 2/3 doctest compatibility 18 | >>> def p(s): 19 | ... if not isinstance(s,str): 20 | ... return s.decode() 21 | ... return s 22 | 23 | >>> b = Buffer() 24 | >>> b.pack("!BHI",1,2,3) 25 | >>> b.offset 26 | 7 27 | >>> b.append(b"0123456789") 28 | >>> b.offset 29 | 17 30 | >>> p(b.hex()) 31 | '0100020000000330313233343536373839' 32 | >>> b.offset = 0 33 | >>> b.unpack("!BHI") 34 | (1, 2, 3) 35 | >>> bytearray(b.get(5)) 36 | bytearray(b'01234') 37 | >>> bytearray(b.get(5)) 38 | bytearray(b'56789') 39 | >>> b.update(7,"2s",b"xx") 40 | >>> b.offset = 7 41 | >>> bytearray(b.get(5)) 42 | bytearray(b'xx234') 43 | """ 44 | 45 | def __init__(self,data=b''): 46 | """ 47 | Initialise Buffer from data 48 | """ 49 | self.data = bytearray(data) 50 | self.offset = 0 51 | 52 | def remaining(self): 53 | """ 54 | Return bytes remaining 55 | """ 56 | return len(self.data) - self.offset 57 | 58 | def get(self,length): 59 | """ 60 | Gen len bytes at current offset (& increment offset) 61 | """ 62 | if length > self.remaining(): 63 | raise BufferError("Not enough bytes [offset=%d,remaining=%d,requested=%d]" % 64 | (self.offset,self.remaining(),length)) 65 | start = self.offset 66 | end = self.offset + length 67 | self.offset += length 68 | return bytes(self.data[start:end]) 69 | 70 | def hex(self): 71 | """ 72 | Return data as hex string 73 | """ 74 | return binascii.hexlify(self.data) 75 | 76 | def pack(self,fmt,*args): 77 | """ 78 | Pack data at end of data according to fmt (from struct) & increment 79 | offset 80 | """ 81 | self.offset += struct.calcsize(fmt) 82 | self.data += struct.pack(fmt,*args) 83 | 84 | def append(self,s): 85 | """ 86 | Append s to end of data & increment offset 87 | """ 88 | self.offset += len(s) 89 | self.data += s 90 | 91 | def update(self,ptr,fmt,*args): 92 | """ 93 | Modify data at offset `ptr` 94 | """ 95 | s = struct.pack(fmt,*args) 96 | self.data[ptr:ptr+len(s)] = s 97 | 98 | def unpack(self,fmt): 99 | """ 100 | Unpack data at current offset according to fmt (from struct) 101 | """ 102 | try: 103 | data = self.get(struct.calcsize(fmt)) 104 | return struct.unpack(fmt,data) 105 | except struct.error as e: 106 | raise BufferError("Error unpacking struct '%s' <%s>" % 107 | (fmt,binascii.hexlify(data).decode())) 108 | 109 | def __len__(self): 110 | return len(self.data) 111 | 112 | if __name__ == '__main__': 113 | import doctest 114 | doctest.testmod() 115 | -------------------------------------------------------------------------------- /dnslib/buffer.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/buffer.pyc -------------------------------------------------------------------------------- /dnslib/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | DNS Client - DiG-like CLI utility. 5 | 6 | Mostly useful for testing. Can optionally compare results from two 7 | nameservers (--diff) or compare results against DiG (--dig). 8 | 9 | Usage: python -m dnslib.client [options|--help] 10 | 11 | See --help for usage. 12 | """ 13 | 14 | from __future__ import print_function 15 | 16 | try: 17 | from subprocess import getoutput 18 | except ImportError: 19 | from commands import getoutput 20 | 21 | import binascii,code,pprint 22 | 23 | from dnslib.dns import DNSRecord,DNSHeader,DNSQuestion,QTYPE 24 | from dnslib.digparser import DigParser 25 | 26 | if __name__ == '__main__': 27 | 28 | import argparse,sys,time 29 | 30 | p = argparse.ArgumentParser(description="DNS Client") 31 | p.add_argument("--server","-s",default="8.8.8.8", 32 | metavar="", 33 | help="Server address:port (default:8.8.8.8:53) (port is optional)") 34 | p.add_argument("--query",action='store_true',default=False, 35 | help="Show query (default: False)") 36 | p.add_argument("--hex",action='store_true',default=False, 37 | help="Dump packet in hex (default: False)") 38 | p.add_argument("--tcp",action='store_true',default=False, 39 | help="Use TCP (default: UDP)") 40 | p.add_argument("--noretry",action='store_true',default=False, 41 | help="Don't retry query using TCP if truncated (default: false)") 42 | p.add_argument("--diff",default="", 43 | help="Compare response from alternate nameserver (format: address:port / default: false)") 44 | p.add_argument("--dig",action='store_true',default=False, 45 | help="Compare result with DiG - if ---diff also specified use alternative nameserver for DiG request (default: false)") 46 | p.add_argument("--short",action='store_true',default=False, 47 | help="Short output - rdata only (default: false)") 48 | p.add_argument("--debug",action='store_true',default=False, 49 | help="Drop into CLI after request (default: false)") 50 | p.add_argument("domain",metavar="", 51 | help="Query domain") 52 | p.add_argument("qtype",metavar="",default="A",nargs="?", 53 | help="Query type (default: A)") 54 | args = p.parse_args() 55 | 56 | # Construct request 57 | q = DNSRecord(q=DNSQuestion(args.domain,getattr(QTYPE,args.qtype))) 58 | 59 | address,_,port = args.server.partition(':') 60 | port = int(port or 53) 61 | 62 | if args.query: 63 | print(";; Sending%s:" % (" (TCP)" if args.tcp else "")) 64 | if args.hex: 65 | print(";; QUERY:",binascii.hexlify(q.pack()).decode()) 66 | print(q) 67 | print() 68 | 69 | a_pkt = q.send(address,port,tcp=args.tcp) 70 | a = DNSRecord.parse(a_pkt) 71 | 72 | if a.header.tc and args.noretry == False: 73 | # Truncated - retry in TCP mode 74 | a_pkt = q.send(address,port,tcp=True) 75 | a = DNSRecord.parse(a_pkt) 76 | 77 | if args.dig or args.diff: 78 | if args.diff: 79 | address,_,port = args.diff.partition(':') 80 | port = int(port or 53) 81 | 82 | if args.dig: 83 | dig = getoutput("dig +qr -p %d %s %s @%s" % ( 84 | port, args.domain, args.qtype, address)) 85 | dig_reply = list(iter(DigParser(dig))) 86 | # DiG might have retried in TCP mode so get last q/a 87 | q_diff = dig_reply[-2] 88 | a_diff = dig_reply[-1] 89 | else: 90 | q_diff = DNSRecord(header=DNSHeader(id=q.header.id), 91 | q=DNSQuestion(args.domain, 92 | getattr(QTYPE,args.qtype))) 93 | q_diff = q 94 | diff = q_diff.send(address,port,tcp=args.tcp) 95 | a_diff = DNSRecord.parse(diff) 96 | if a_diff.header.tc and args.noretry == False: 97 | diff = q_diff.send(address,port,tcp=True) 98 | a_diff = DNSRecord.parse(diff) 99 | 100 | if args.short: 101 | print(a.short()) 102 | else: 103 | print(";; Got answer:") 104 | if args.hex: 105 | print(";; RESPONSE:",binascii.hexlify(a_pkt).decode()) 106 | if args.diff and not args.dig: 107 | print(";; DIFF :",binascii.hexlify(diff).decode()) 108 | print(a) 109 | print() 110 | 111 | if args.dig or args.diff: 112 | if q != q_diff: 113 | print(";;; ERROR: Diff Question differs") 114 | for (d1,d2) in q.diff(q_diff): 115 | if d1: 116 | print(";; - %s" % d1) 117 | if d2: 118 | print(";; + %s" % d2) 119 | if a != a_diff: 120 | print(";;; ERROR: Diff Response differs") 121 | for (d1,d2) in a.diff(a_diff): 122 | if d1: 123 | print(";; - %s" % d1) 124 | if d2: 125 | print(";; + %s" % d2) 126 | 127 | if args.debug: 128 | code.interact(local=locals()) 129 | 130 | -------------------------------------------------------------------------------- /dnslib/digparser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | digparser 5 | --------- 6 | 7 | Encode/decode DNS packets from DiG textual representation. Parses 8 | question (if present: +qr flag) & answer sections and returns list 9 | of DNSRecord objects. 10 | 11 | Unsupported RR types are skipped (this is different from the packet 12 | parser which will store and encode the RDATA as a binary blob) 13 | 14 | >>> dig = os.path.join(os.path.dirname(__file__),"test","dig","google.com-A.dig") 15 | >>> with open(dig) as f: 16 | ... l = DigParser(f) 17 | ... for record in l: 18 | ... print('---') 19 | ... print(repr(record)) 20 | --- 21 | 22 | 23 | --- 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | >>> dig = os.path.join(os.path.dirname(__file__),"test","dig","google.com-ANY.dig") 44 | >>> with open(dig) as f: 45 | ... l = DigParser(f) 46 | ... for record in l: 47 | ... print('---') 48 | ... print(repr(record)) 49 | --- 50 | 51 | 52 | --- 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | """ 71 | 72 | from __future__ import print_function 73 | 74 | import glob,os.path,string 75 | 76 | from dnslib.lex import WordLexer 77 | from dnslib.dns import (DNSRecord,DNSHeader,DNSQuestion,DNSError, 78 | RR,RD,RDMAP,QR,RCODE,CLASS,QTYPE) 79 | 80 | class DigParser: 81 | 82 | """ 83 | Parse Dig output 84 | """ 85 | 86 | def __init__(self,dig,debug=False): 87 | self.debug = debug 88 | self.l = WordLexer(dig) 89 | self.l.commentchars = ';' 90 | self.l.nltok = ('NL',None) 91 | self.i = iter(self.l) 92 | 93 | def parseHeader(self,l1,l2): 94 | _,_,_,opcode,_,status,_,_id = l1.split() 95 | _,flags,_ = l2.split(';') 96 | header = DNSHeader(id=int(_id),bitmap=0) 97 | header.opcode = getattr(QR,opcode.rstrip(',')) 98 | header.rcode = getattr(RCODE,status.rstrip(',')) 99 | for f in ('qr','aa','tc','rd','ra'): 100 | if f in flags: 101 | setattr(header,f,1) 102 | return header 103 | 104 | def expect(self,expect): 105 | t,val = next(self.i) 106 | if t != expect: 107 | raise ValueError("Invalid Token: %s (expecting: %s)" % (t,expect)) 108 | return val 109 | 110 | def parseQuestions(self,q,dns): 111 | for qname,qclass,qtype in q: 112 | dns.add_question(DNSQuestion(qname, 113 | getattr(QTYPE,qtype), 114 | getattr(CLASS,qclass))) 115 | 116 | def parseAnswers(self,a,auth,ar,dns): 117 | sect_map = {'a':'add_answer','auth':'add_auth','ar':'add_ar'} 118 | for sect in 'a','auth','ar': 119 | f = getattr(dns,sect_map[sect]) 120 | for rr in locals()[sect]: 121 | rname,ttl,rclass,rtype = rr[:4] 122 | rdata = rr[4:] 123 | rd = RDMAP.get(rtype,RD) 124 | try: 125 | if rd == RD and \ 126 | any([ x not in string.hexdigits for x in rdata[-1]]): 127 | # Only support hex encoded data for fallback RD 128 | pass 129 | else: 130 | f(RR(rname=rname, 131 | ttl=int(ttl), 132 | rtype=getattr(QTYPE,rtype), 133 | rclass=getattr(CLASS,rclass), 134 | rdata=rd.fromZone(rdata))) 135 | except DNSError as e: 136 | if self.debug: 137 | print("DNSError:",e,rr) 138 | else: 139 | # Skip records we dont understand 140 | pass 141 | 142 | def __iter__(self): 143 | return self.parse() 144 | 145 | def parse(self): 146 | dns = None 147 | section = None 148 | paren = False 149 | rr = [] 150 | try: 151 | while True: 152 | tok,val = next(self.i) 153 | if tok == 'COMMENT': 154 | if 'Sending:' in val or 'Got answer:' in val: 155 | if dns: 156 | self.parseQuestions(q,dns) 157 | self.parseAnswers(a,auth,ar,dns) 158 | yield(dns) 159 | dns = DNSRecord() 160 | q,a,auth,ar = [],[],[],[] 161 | elif val.startswith('; ->>HEADER<<-'): 162 | self.expect('NL') 163 | val2 = self.expect('COMMENT') 164 | dns.header = self.parseHeader(val,val2) 165 | elif val.startswith('; QUESTION'): 166 | section = q 167 | elif val.startswith('; ANSWER'): 168 | section = a 169 | elif val.startswith('; AUTHORITY'): 170 | section = auth 171 | elif val.startswith('; ADDITIONAL'): 172 | section = ar 173 | elif val.startswith(';') or tok[1].startswith('<<>>'): 174 | pass 175 | elif dns and section == q: 176 | q.append(val.split()) 177 | elif tok == 'ATOM': 178 | if val == '(': 179 | paren = True 180 | elif val == ')': 181 | paren = False 182 | else: 183 | rr.append(val) 184 | elif tok == 'NL' and not paren and rr: 185 | if self.debug: 186 | print(">>",rr) 187 | section.append(rr) 188 | rr = [] 189 | except StopIteration: 190 | if rr: 191 | self.section.append(rr) 192 | if dns: 193 | self.parseQuestions(q,dns) 194 | self.parseAnswers(a,auth,ar,dns) 195 | yield(dns) 196 | 197 | if __name__ == '__main__': 198 | 199 | import argparse,doctest,sys 200 | 201 | p = argparse.ArgumentParser(description="DigParser Test") 202 | p.add_argument("--dig",action='store_true',default=False, 203 | help="Parse DiG output (stdin)") 204 | p.add_argument("--debug",action='store_true',default=False, 205 | help="Debug output") 206 | 207 | args = p.parse_args() 208 | 209 | if args.dig: 210 | l = DigParser(sys.stdin,args.debug) 211 | for record in l: 212 | print(repr(record)) 213 | else: 214 | doctest.testmod() 215 | -------------------------------------------------------------------------------- /dnslib/digparser.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/digparser.pyc -------------------------------------------------------------------------------- /dnslib/dns.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyunbin/dowsDNS/91f4222c717e7cf04e1e2f0c04be929ac4c2dc2e/dnslib/dns.pyc -------------------------------------------------------------------------------- /dnslib/fixedresolver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | FixedResolver - example resolver which responds with fixed response 5 | to all requests 6 | """ 7 | 8 | from __future__ import print_function 9 | 10 | import copy 11 | 12 | from dnslib import RR 13 | from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger 14 | 15 | class FixedResolver(BaseResolver): 16 | """ 17 | Respond with fixed response to all requests 18 | """ 19 | def __init__(self,zone): 20 | # Parse RRs 21 | self.rrs = RR.fromZone(zone) 22 | 23 | def resolve(self,request,handler): 24 | reply = request.reply() 25 | qname = request.q.qname 26 | # Replace labels with request label 27 | for rr in self.rrs: 28 | a = copy.copy(rr) 29 | a.rname = qname 30 | reply.add_answer(a) 31 | return reply 32 | 33 | if __name__ == '__main__': 34 | 35 | import argparse,sys,time 36 | 37 | p = argparse.ArgumentParser(description="Fixed DNS Resolver") 38 | p.add_argument("--response","-r",default=". 60 IN A 127.0.0.1", 39 | metavar="", 40 | help="DNS response (zone format) (default: 127.0.0.1)") 41 | p.add_argument("--zonefile","-f", 42 | metavar="", 43 | help="DNS response (zone file, '-' for stdin)") 44 | p.add_argument("--port","-p",type=int,default=53, 45 | metavar="", 46 | help="Server port (default:53)") 47 | p.add_argument("--address","-a",default="", 48 | metavar="
", 49 | help="Listen address (default:all)") 50 | p.add_argument("--udplen","-u",type=int,default=0, 51 | metavar="", 52 | help="Max UDP packet length (default:0)") 53 | p.add_argument("--tcp",action='store_true',default=False, 54 | help="TCP server (default: UDP only)") 55 | p.add_argument("--log",default="request,reply,truncated,error", 56 | help="Log hooks to enable (default: +request,+reply,+truncated,+error,-recv,-send,-data)") 57 | p.add_argument("--log-prefix",action='store_true',default=False, 58 | help="Log prefix (timestamp/handler/resolver) (default: False)") 59 | args = p.parse_args() 60 | 61 | if args.zonefile: 62 | if args.zonefile == '-': 63 | args.response = sys.stdin 64 | else: 65 | args.response = open(args.zonefile) 66 | 67 | resolver = FixedResolver(args.response) 68 | logger = DNSLogger(args.log,args.log_prefix) 69 | 70 | print("Starting Fixed Resolver (%s:%d) [%s]" % ( 71 | args.address or "*", 72 | args.port, 73 | "UDP/TCP" if args.tcp else "UDP")) 74 | 75 | for rr in resolver.rrs: 76 | print(" | ",rr.toZone().strip(),sep="") 77 | print() 78 | 79 | if args.udplen: 80 | DNSHandler.udplen = args.udplen 81 | 82 | udp_server = DNSServer(resolver, 83 | port=args.port, 84 | address=args.address, 85 | logger=logger) 86 | udp_server.start_thread() 87 | 88 | if args.tcp: 89 | tcp_server = DNSServer(resolver, 90 | port=args.port, 91 | address=args.address, 92 | tcp=True, 93 | logger=logger) 94 | tcp_server.start_thread() 95 | 96 | while udp_server.isAlive(): 97 | time.sleep(1) 98 | 99 | -------------------------------------------------------------------------------- /dnslib/intercept.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | InterceptResolver - proxy requests to upstream server 5 | (optionally intercepting) 6 | 7 | """ 8 | from __future__ import print_function 9 | 10 | import binascii,copy,socket,struct,sys 11 | 12 | from dnslib import DNSRecord,RR,QTYPE,RCODE,parse_time 13 | from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger 14 | from dnslib.label import DNSLabel 15 | 16 | class InterceptResolver(BaseResolver): 17 | 18 | """ 19 | Intercepting resolver 20 | 21 | Proxy requests to upstream server optionally intercepting requests 22 | matching local records 23 | """ 24 | 25 | def __init__(self,address,port,ttl,intercept,skip,nxdomain,timeout=0): 26 | """ 27 | address/port - upstream server 28 | ttl - default ttl for intercept records 29 | intercept - list of wildcard RRs to respond to (zone format) 30 | skip - list of wildcard labels to skip 31 | nxdomain - list of wildcard labels to retudn NXDOMAIN 32 | timeout - timeout for upstream server 33 | """ 34 | self.address = address 35 | self.port = port 36 | self.ttl = parse_time(ttl) 37 | self.skip = skip 38 | self.nxdomain = nxdomain 39 | self.timeout = timeout 40 | self.zone = [] 41 | for i in intercept: 42 | if i == '-': 43 | i = sys.stdin.read() 44 | for rr in RR.fromZone(i,ttl=self.ttl): 45 | self.zone.append((rr.rname,QTYPE[rr.rtype],rr)) 46 | 47 | def resolve(self,request,handler): 48 | reply = request.reply() 49 | qname = request.q.qname 50 | qtype = QTYPE[request.q.qtype] 51 | # Try to resolve locally unless on skip list 52 | if not any([qname.matchGlob(s) for s in self.skip]): 53 | for name,rtype,rr in self.zone: 54 | if qname.matchGlob(name) and (qtype in (rtype,'ANY','CNAME')): 55 | a = copy.copy(rr) 56 | a.rname = qname 57 | reply.add_answer(a) 58 | # Check for NXDOMAIN 59 | if any([qname.matchGlob(s) for s in self.nxdomain]): 60 | reply.header.rcode = getattr(RCODE,'NXDOMAIN') 61 | return reply 62 | # Otherwise proxy 63 | if not reply.rr: 64 | try: 65 | if handler.protocol == 'udp': 66 | proxy_r = request.send(self.address,self.port, 67 | timeout=self.timeout) 68 | else: 69 | proxy_r = request.send(self.address,self.port, 70 | tcp=True,timeout=self.timeout) 71 | reply = DNSRecord.parse(proxy_r) 72 | except socket.timeout: 73 | reply.header.rcode = getattr(RCODE,'NXDOMAIN') 74 | 75 | return reply 76 | 77 | if __name__ == '__main__': 78 | 79 | import argparse,sys,time 80 | 81 | p = argparse.ArgumentParser(description="DNS Intercept Proxy") 82 | p.add_argument("--port","-p",type=int,default=53, 83 | metavar="", 84 | help="Local proxy port (default:53)") 85 | p.add_argument("--address","-a",default="", 86 | metavar="
", 87 | help="Local proxy listen address (default:all)") 88 | p.add_argument("--upstream","-u",default="8.8.8.8:53", 89 | metavar="", 90 | help="Upstream DNS server:port (default:8.8.8.8:53)") 91 | p.add_argument("--tcp",action='store_true',default=False, 92 | help="TCP proxy (default: UDP only)") 93 | p.add_argument("--intercept","-i",action="append", 94 | metavar="", 95 | help="Intercept requests matching zone record (glob) ('-' for stdin)") 96 | p.add_argument("--skip","-s",action="append", 97 | metavar="