├── README.md ├── archlinux ├── PKGBUILD └── beautifuldnsd.install ├── beautifuldnsd ├── beautifuldnsd.yaml └── systemd ├── beautifuldnsd.service └── tmpfiles.conf /README.md: -------------------------------------------------------------------------------- 1 | DNS 加速代理 2 | ==== 3 | 4 | 原理介绍 5 | ---- 6 | 7 | 为了上网,为了解析域名,我们需要 DNS 服务器所提供的服务。 8 | 9 | 通常,联网之后 ISP(互联网服务提供商,如中国电信、中国联通等)会分配一个他们自己的、经常是只服务于某个地区的 DNS 服务器地址。但是它们经常有以下问题: 10 | 11 | 1. 对不存在的域名进行劫持,返回自家的导航页面地址。对于浏览器来说还好点,毕竟用户一看就知道是怎么回事,虽然很讨厌就对了。而对于程序,以及非网页用途,这种功能就可能会导致问题了。比如火狐在地址栏直接输入不像域名的内容(如「google」),火狐会尝试添加常见前后缀,也会向搜索引擎询问是否有足够的自信判断用户想去哪里,或者直接显示搜索结果。(这个 dnsmasq 也可以解决) 12 | 2. 对于不常访问的域名经常返回服务器失败的信息,或者超时。虽然 ISP 的 DNS 服务器通常只服务很少的用户,但是它们经常处理不过来。 13 | 3. 有些 ISP 的 DNS 会返回奇怪的结果。比如对某些 IP 的 PTR 记录总是返回「localhost」。 14 | 15 | 用户也可以选择使用国内外的公共 DNS 服务器,比如 114.114.114.114 或者 Google 的 8.8.8.8。但是它们也有自己的问题: 16 | 17 | 1. 国外服务器的延迟比较高。 18 | 2. 即使支持相关 DNS 扩展,但是它们还是不能根据地域返回离用户最近的 DNS 节点。这对访问大型网站非常有用,比如淘宝、百度。以及一些使用第三方 CDN 服务的网站,比如使用又拍云、七牛的网站。 19 | 20 | 本程序为了综合不同 DNS 服务提供方的优点、避免它们的缺点,而使用了如下方案: 21 | 22 | 1. 处理掉已知的虚假应答 23 | 2. 向不同服务器并发发起请求,先收到谁的应答就使用谁的,但是 ISP 的 DNS 服务器享有一定的优先权。这样,如果 ISP 的 DNS 服务器能够及时响应,那么就会使用它的结果,否则使用公共 DNS 服务器的结果。有需要使用 CDN 的网站通常用户访问非常多,ISP 的 DNS 服务器能够及时响应这些请求。 24 | 25 | 配置及使用 26 | ---- 27 | 28 | 本程序应当在实际需要访问的地方运行,比如运行于用户的电脑上,或者用户的网关/路由器上。 29 | 30 | 本程序需要预先安装以下软件: 31 | 32 | 1. Python 3.4 或以上 33 | 2. Python 的 dnslib 库 34 | 35 | Arch Linux 用户可以直接从 AUR 安装「beautifuldnsd」软件包(感谢 lilydjwg 的打包)。 36 | 37 | 本程序的配置请见「beautifuldnsd.yaml」中的注释。 38 | 39 | 使用「beautifuldnsd --help」来查看可用的命令行选项。 40 | 41 | 本程序不具有缓存功能。请配合 dnsmasq 使用以取得最佳效果。 42 | 43 | 测试结果 44 | ---- 45 | 46 | 如下是某次的统计结果: 47 | 48 | : count/avg/mdev/failed: 59828/337.3/1210.0/986 49 | : count/avg/mdev/win/timeout/failed: 61454/3137.1/261927.7/49926/5693/1075 50 | : count/avg/mdev/win/timeout/failed: 20760/2314.5/3710.8/4520/3865/110 51 | : count/avg/mdev/win/timeout/failed: 14455/2839.4/57274.5/3683/2689/0 52 | : count/avg/mdev/win/timeout/failed: 7392/3215.4/4149.1/713/1985/19 53 | 54 | 总共处理了近六万 DNS 请求,其中有近一千个失败了。平均响应延迟是三百多毫秒,标准差是一千多。 55 | 56 | 第一个上游是 ISP 的 DNS 服务器,总共发送了六万多请求,超时的近六千,服务器返回失败的有一千多。平均响应延迟高达三秒多,标准差更是25万之甚(说明延迟波动非常大,有的请求能够很快返回结果,有的会等非常久)。本程序从这个 DNS 服务器返回了近五万的应答结果。 57 | 58 | 余下的也是类似地解读。 59 | 60 | 可以看出,本程序的平均响应时间比所有上游更稳定、也更迅速。 61 | 62 | 贡献 63 | ---- 64 | 65 | 本程序为公有领域的作品,你可以自由地使用。欢迎提交补丁。 66 | 67 | -------------------------------------------------------------------------------- /archlinux/PKGBUILD: -------------------------------------------------------------------------------- 1 | pkgname=beautifuldnsd 2 | pkgver=0.7 3 | pkgrel=1 4 | pkgdesc="Quick and clean DNS proxy, especially useful for users with poor ISPs" 5 | arch=('any') 6 | url="https://github.com/programmervy/beautifuldnsd" 7 | license=("public domain") 8 | depends=('python>=3.4' 'python-dnslib' 'python-yaml') 9 | optdepends=( 10 | 'dnsmasq: DNS cache' 11 | 'python-pygeoip: China only DNS configuration' 12 | 'geoip-database: provides a database for geoip lookups' 13 | 'python-pysocks: socks5 proxy support' 14 | ) 15 | backup=("etc/${pkgname}.yaml") 16 | install=beautifuldnsd.install 17 | source=(${pkgname}-${pkgver}.tar.gz::${url}/archive/v${pkgver}.tar.gz) 18 | sha512sums=('1516c34da9b66e492cd85b9a71c01682a0ffe90c00b1e8aaf6eacbc2aad2e8482653042eb476438aa92a837ced6d123bd695bab0a55cce1d292bd50de5668b46') 19 | 20 | package() { 21 | cd ~/workspace/github/beautifuldnsd 22 | install -D -m644 systemd/beautifuldnsd.service "${pkgdir}/usr/lib/systemd/system/${pkgname}.service" 23 | install -D -m644 systemd/tmpfiles.conf "${pkgdir}/usr/lib/tmpfiles.d/${pkgname}.conf" 24 | install -D -m644 beautifuldnsd.yaml "${pkgdir}/etc/${pkgname}.yaml" 25 | install -D -m755 beautifuldnsd "${pkgdir}/usr/bin/${pkgname}" 26 | } 27 | 28 | -------------------------------------------------------------------------------- /archlinux/beautifuldnsd.install: -------------------------------------------------------------------------------- 1 | post_install() { 2 | useradd -r -U -d /var/lib/beautifuldnsd -s /bin/false beautifuldnsd &>/dev/null 3 | chown -R beautifuldnsd:beautifuldnsd /etc/beautifuldnsd.yaml 4 | } 5 | 6 | post_upgrade() { 7 | post_install $1 8 | } 9 | 10 | pre_remove() { 11 | userdel beautifuldnsd &>/dev/null 12 | groupdel beautifuldnsd &>/dev/null 13 | rm -rf var/lib/beautifuldnsd 14 | } 15 | -------------------------------------------------------------------------------- /beautifuldnsd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # FIXME: bug with dig TXT 20120113._domainkey.gmail.com 4 | # https://bitbucket.org/paulc/dnslib/pull-request/9/update-dnspy-class-txt-to-handle-multiple/diff 5 | # FIXME: bug with dig DNSKEY mozilla.net 6 | 7 | import sys 8 | import os 9 | import base64 10 | import logging 11 | import asyncio 12 | import socket 13 | import ipaddress 14 | import struct 15 | import time 16 | import signal 17 | from functools import partial 18 | import dbm 19 | import shelve 20 | 21 | import dnslib 22 | import yaml 23 | try: 24 | from yaml import CLoader as Loader 25 | except ImportError: 26 | from yaml import Loader 27 | try: 28 | import pygeoip 29 | except ImportError: 30 | pygeoip = None 31 | 32 | try: 33 | import socks as socksproxy 34 | except ImportError: 35 | socksproxy = None 36 | 37 | def loadyaml(src): 38 | return yaml.load(src, Loader=Loader) 39 | 40 | # from stats.py begin 41 | import math 42 | 43 | NaN = float('NaN') 44 | 45 | class Stat: 46 | '''A class that accepts numbers and provides stats info. 47 | 48 | Available properties are: 49 | - n: number of numbers that have been added 50 | - sum: sum 51 | - avg: average or raise ZeroDivisionError if nothing has been added yet 52 | - min: mimimum or None if nothing has been added yet 53 | - max: maximum or None if nothing has been added yet 54 | - mdev: standard deviation or raise ZeroDivisionError if nothing has been added yet 55 | - sum2: square sum 56 | ''' 57 | n = 0 58 | sum = 0 59 | sum2 = 0 60 | min = max = None 61 | 62 | @property 63 | def avg(self): 64 | '''average or 0''' 65 | try: 66 | return self.sum / self.n 67 | except ZeroDivisionError: 68 | return NaN 69 | 70 | @property 71 | def mdev(self): 72 | '''standard deviation or raise ZeroDivisionError if nothing has been added yet''' 73 | try: 74 | return math.sqrt(self.sum2 / self.n - self.avg ** 2) 75 | except ZeroDivisionError: 76 | return NaN 77 | 78 | def add(self, x): 79 | '''add a number to stats''' 80 | self.n += 1 81 | self.sum += x 82 | self.sum2 += x ** 2 83 | if self.min is None: 84 | self.min = self.max = x 85 | else: 86 | if x < self.min: 87 | self.min = x 88 | elif x > self.max: 89 | self.max = x 90 | 91 | def __str__(self): 92 | avg = self.avg 93 | mdev = self.mdev 94 | min = self.min 95 | max = self.max 96 | return 'min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f' % (min, avg, max, mdev) 97 | 98 | def __repr__(self): 99 | return '<%s.%s: %s>' % ( 100 | self.__class__.__module__, 101 | self.__class__.__name__, 102 | self.__str__(), 103 | ) 104 | # from stats.py end 105 | 106 | logger = logging.getLogger('beautifuldnsd') 107 | # TODO: handle truncated reply 108 | # TODO: command to re-discover bogus IP, add/remove servers 109 | SERVFAIL = dnslib.RCODE.SERVFAIL 110 | REFUSED = dnslib.RCODE.REFUSED 111 | NOERROR = dnslib.RCODE.NOERROR 112 | BOGUS_IPS = None 113 | BOGUS_IPS_DB = None 114 | POLLUTION_IPS = None 115 | POLLUTED_DOMAINS = None 116 | GEOIP = None 117 | 118 | def repr_qtype(qtype): 119 | return dnslib.QTYPE.get(qtype) 120 | 121 | async def async_pass(): 122 | pass 123 | 124 | class SwitchServer(Exception): pass 125 | 126 | class NotChina(Exception): pass 127 | 128 | class NotConnected(Exception): pass 129 | 130 | def config_logging(level=logging.WARN, style=None): 131 | ''' 132 | ``style``: terminal or plain 133 | ''' 134 | if style is None: 135 | if os.isatty(sys.stderr.fileno()): 136 | style = 'terminal' 137 | else: 138 | style = 'plain' 139 | 140 | if style == 'plain': 141 | logging.basicConfig( 142 | level=level, 143 | format='%(levelname)s: %(message)s', 144 | ) 145 | elif style == 'terminal': 146 | try: 147 | from nicelogger import enable_pretty_logging 148 | enable_pretty_logging(level) 149 | except ImportError: 150 | logging.basicConfig( 151 | level=level, 152 | format='%(asctime) s%(levelname)s: %(message)s', 153 | datefmt='[%Y-%m-%d %H:%M:%S]', 154 | ) 155 | else: 156 | raise ValueError('bad logging style %r' % style) 157 | 158 | def ip_family(ip): 159 | a = ipaddress.ip_address(ip) 160 | if a.version == 4: 161 | return socket.AF_INET 162 | elif a.version == 6: 163 | return socket.AF_INET6 164 | else: 165 | raise ValueError('unsupport IP address: %r', a) 166 | 167 | def time_as_bytes(): 168 | return struct.pack('' % ( 329 | self.__class__.__name__, 330 | self.format_address(), 331 | ) 332 | 333 | def format_address(self): 334 | return '%s:%s:%d' % ( 335 | 'TCP' if self.type is socket.SOCK_STREAM else 'UDP', 336 | self.address[0], self.address[1]) 337 | 338 | def __del__(self): 339 | self.close() 340 | 341 | def close(self): 342 | if self.sock: 343 | self.loop.remove_reader(self.sock.fileno()) 344 | self.sock.close() 345 | self.sock = None 346 | 347 | def record_time(self, t): 348 | self._stat.add(t * 1000) 349 | 350 | def log_stat(self): 351 | logger.info('%s: count/avg/mdev/failed: %d/%.1f/%.1f/%d', 352 | self, 353 | self._stat.n, self._stat.avg, self._stat.mdev, 354 | self.failed_times) 355 | 356 | class UDPDNSServer(DNSCommon): 357 | sock = None 358 | type = socket.SOCK_DGRAM 359 | 360 | def __init__(self, address, upstreams, *, 361 | concurrent_delay=None): 362 | self.address = address 363 | self.loop = loop = asyncio.get_event_loop() 364 | self.upstreams = upstreams 365 | self.concurrent_delay = concurrent_delay 366 | 367 | self.sock = s = socket.socket( 368 | ip_family(address[0]), socket.SOCK_DGRAM) 369 | s.setblocking(False) 370 | s.bind(address) 371 | 372 | loop.add_reader(s.fileno(), self.on_request) 373 | 374 | def on_request(self): 375 | co = self.handle_request() 376 | fu = asyncio.ensure_future(co) 377 | fu.add_done_callback(lambda fu: fu.result()) 378 | 379 | async def handle_request(self): 380 | s = self.sock 381 | try: 382 | data, addr = s.recvfrom(40960) 383 | except BlockingIOError: 384 | return 385 | 386 | start = time.time() 387 | request = dnslib.DNSRecord.parse(data) 388 | if logger.isEnabledFor(logging.DEBUG): 389 | logger.debug('%r request for %s of %s', addr, 390 | repr_qtype(request.q.qtype), request.q.qname) 391 | 392 | no_more = False 393 | failed = False 394 | skipping = is_polluted_domain(request) 395 | notchina = False 396 | concurrent_delay = self.concurrent_delay 397 | it = iter(self.upstreams) 398 | futures = {} 399 | is_esni = request.q.qtype == 16 and \ 400 | request.q.qname.label[0] == b'_esni' 401 | 402 | while True: 403 | try: 404 | while True: 405 | server = next(it) 406 | if server is None: 407 | continue 408 | if notchina and server.china_only: 409 | continue 410 | if is_esni and not server.accept_esni: 411 | continue 412 | if not skipping or server.on_pollution != 'switch': 413 | break 414 | logger.debug('%s: trying server %s', self, server) 415 | fu = asyncio.ensure_future(server.query(request)) 416 | futures[fu] = server 417 | except StopIteration: 418 | concurrent_delay = None 419 | no_more = True 420 | if not futures: 421 | failed = True 422 | break 423 | 424 | done, pending = await asyncio.wait( 425 | futures.keys(), 426 | timeout = concurrent_delay, 427 | return_when=asyncio.FIRST_COMPLETED) 428 | 429 | if no_more and not done: 430 | failed = True 431 | break 432 | 433 | got_it = False 434 | for fu in done: 435 | server = futures.pop(fu) 436 | ex = fu.exception() 437 | if ex is None: 438 | ans, parsed = fu.result() 439 | if parsed.header.rcode == SERVFAIL: 440 | continue 441 | elif parsed.header.rcode == REFUSED: 442 | logger.warning('%s: refused for %s', self, parsed.q) 443 | continue 444 | elif is_bogus_nxdomain(parsed): 445 | logger.warning('%s: bogus answer, ignored.', self) 446 | continue 447 | elif parsed.header.tc: 448 | logger.warning('%s: truncated answer, ignored:\n%s', 449 | self, parsed) 450 | continue 451 | server.wins += 1 452 | logger.debug('%s: %r from %s', self, parsed.a, server) 453 | got_it = True 454 | break 455 | elif isinstance(ex, SwitchServer): 456 | skipping = True 457 | elif isinstance(ex, NotChina): 458 | notchina = True 459 | elif isinstance(ex, (asyncio.TimeoutError, OSError, NotConnected)): 460 | pass 461 | else: 462 | raise ex 463 | if got_it: 464 | break 465 | 466 | if failed: 467 | logger.warning('%s: all servers failed to reply for %r', self, request.q) 468 | ans = reply_with(request, SERVFAIL) 469 | elif is_bogus_nxdomain(parsed): 470 | logger.warning('%s: bogus nxdomain, sending back nxdomain to %r', 471 | self, addr) 472 | ans = reply_with(request, dnslib.RCODE.NXDOMAIN) 473 | else: 474 | logger.debug('%s: sending back DNS answer to %r:\n%s', 475 | self, addr, parsed) 476 | 477 | s.sendto(ans, addr) 478 | end = time.time() 479 | if failed: 480 | self.failed_times += 1 481 | else: 482 | self.record_time(end - start) 483 | for fu in futures: 484 | fu.add_done_callback(catch_timeout) 485 | 486 | class DNSClient(DNSCommon): 487 | #TODO: connect on demand and catch timeout and close (for TCP) 488 | sock = None 489 | connecting = None 490 | timeouts = 0 491 | wins = 0 492 | subnet = None 493 | proxy = None 494 | _connected = False 495 | 496 | def __new__(cls, *args, proto='udp', **kwargs): 497 | if proto == 'udp': 498 | C = UDPDNSClient 499 | elif proto == 'tcp': 500 | C = TCPDNSClient 501 | else: 502 | raise ValueError('unknown protocol %r' % proto) 503 | 504 | instance = super().__new__(C) 505 | return instance 506 | 507 | def __init__(self, ip, port=53, proto='udp', 508 | *, timeout=10, on_pollution='skip', 509 | subnet=None, 510 | proxy=None, 511 | mark=0, 512 | accept_esni=False, 513 | china_only=False, 514 | discover_bogus_ip=False, 515 | discover_interval=600): 516 | self.loop = asyncio.get_event_loop() 517 | self.timeout = timeout 518 | self.address = ip, port 519 | self.type = socket.SOCK_DGRAM if proto == 'udp' else socket.SOCK_STREAM 520 | 521 | if subnet is not None: 522 | self.subnet = ipaddress.IPv4Network(subnet) 523 | if socksproxy: 524 | self.proxy = proxy 525 | else: 526 | logger.error('PySocks not available, proxy ignored.') 527 | self.mark = mark 528 | self.accept_esni = accept_esni 529 | 530 | self._id_dict = {} 531 | self.connect() 532 | if on_pollution in ('skip', 'switch', 'noaction'): 533 | self.on_pollution = on_pollution 534 | else: 535 | raise ValueError('unknown option %r for on_pollution' % on_pollution) 536 | if discover_bogus_ip: 537 | self.discover_bogus_ip(discover_interval) 538 | # or we'll add our random domains into db 539 | self.requireAR = not discover_bogus_ip 540 | self.china_only = china_only 541 | 542 | def __repr__(self): 543 | if self.mark: 544 | return '<%s [%s](%d)>' % ( 545 | self.__class__.__name__, 546 | self.format_address(), 547 | self.mark, 548 | ) 549 | else: 550 | return '<%s [%s]>' % ( 551 | self.__class__.__name__, 552 | self.format_address(), 553 | ) 554 | 555 | def discover_bogus_ip(self, interval): 556 | asyncio.ensure_future(get_bogus_nxdomain_one(self, interval)) 557 | 558 | def connect(self): 559 | self.close() 560 | self._connected = False 561 | 562 | if self.proxy: 563 | self.sock = socksproxy.socksocket(ip_family(self.address[0]), self.type) 564 | self.sock.set_proxy(socksproxy.SOCKS5, self.proxy[0], self.proxy[1]) 565 | else: 566 | self.sock = socket.socket(ip_family(self.address[0]), self.type) 567 | 568 | self.sock.setblocking(False) 569 | logger.debug('%s: connecting', self) 570 | if self.proxy: 571 | try: 572 | self.sock.connect(self.address) 573 | except ConnectionRefusedError: 574 | logger.error('%s: ConnectionRefusedError', self) 575 | return 576 | self.connecting = asyncio.Task(async_pass()) 577 | else: 578 | self.connecting = asyncio.Task(self.loop.sock_connect(self.sock, self.address)) 579 | self.connecting.add_done_callback(self.connected) 580 | 581 | def connected(self, fu): 582 | self.connecting = None 583 | self._connected = True 584 | try: 585 | fu.result() 586 | except TimeoutError: 587 | self.connect() 588 | except OSError as e: 589 | if e.errno == 101: # Network is unreachable 590 | logger.error('%s: Network is unreachable', self) 591 | return 592 | else: 593 | raise 594 | logger.info('%s: connected', self) 595 | if self.mark: 596 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_MARK, self.mark) 597 | self.watch_answers() 598 | 599 | def watch_answers(self): 600 | self.loop.add_reader(self.sock.fileno(), self.on_answer) 601 | 602 | def before_query(self): 603 | pass 604 | 605 | def _may_add_ecs(self, req): 606 | if self.subnet is None: 607 | return req 608 | 609 | subnet = self.subnet 610 | plen = subnet.prefixlen 611 | addr = int(subnet.network_address) 612 | data = struct.pack('!HBBI', 1, plen, 0, addr) 613 | drop_n = (32 - plen) // 8 614 | data = data[:-drop_n] 615 | 616 | option = dnslib.EDNSOption(8, data) 617 | r = dnslib.DNSRecord.parse(req.pack()) 618 | if r.ar and r.ar[0].rtype == 41: 619 | r.ar[0].rdata.append(option) 620 | else: 621 | edns0 = dnslib.EDNS0(flags='do', udp_len=4096, opts=[option]) 622 | r.add_ar(edns0) 623 | return r 624 | 625 | async def query(self, request): 626 | ''' 627 | may raise asyncio.TimeoutError 628 | ''' 629 | self.before_query() 630 | 631 | if self.connecting: 632 | await self.connecting 633 | if not self._connected: 634 | raise NotConnected 635 | 636 | request = self._may_add_ecs(request) 637 | data = self.prepare_request(request) 638 | s = e = time.time() 639 | do_record = True 640 | dns = None 641 | try: 642 | while True: 643 | timeleft = s + self.timeout - e 644 | try: 645 | await self.loop.sock_sendall(self.sock, data) 646 | ans, dns = await asyncio.wait_for( 647 | self.recv_answer(request), 648 | timeout = timeleft) 649 | break 650 | except (ConnectionError, BrokenPipeError): 651 | logger.warning('%s: connection error, reconnecting', self, exc_info=True) 652 | self.connect() 653 | await self.connecting 654 | continue 655 | except asyncio.TimeoutError: 656 | if logger.isEnabledFor(logging.INFO): 657 | logger.info('%s: timed out for %s of %s', self, 658 | repr_qtype(request.q.qtype), request.q.qname) 659 | do_record = False 660 | self.timeouts += 1 661 | # propagate further 662 | raise 663 | except OSError as e: 664 | do_record = False 665 | if e.errno in (22, 89): 666 | # 22: Invalid argument 667 | # 89: Destination address required (not connected) 668 | logger.error('%s: network down', self) 669 | self.connect() 670 | ans = reply_with(request, SERVFAIL) 671 | dns = dnslib.DNSRecord.parse(ans) 672 | break 673 | else: 674 | raise 675 | finally: 676 | e = time.time() 677 | finally: 678 | if do_record: 679 | if dns and dns.header.rcode == SERVFAIL: 680 | logger.warning('%s: server failed for %s of %s', self, 681 | repr_qtype(request.q.qtype), request.q.qname) 682 | self.failed_times += 1 683 | self.record_time(e-s) 684 | 685 | return ans, dns 686 | 687 | async def recv_answer(self, req): 688 | fu = asyncio.Future() 689 | id = req.header.id 690 | self._id_dict[id] = fu 691 | try: 692 | return (await fu) 693 | finally: 694 | try: 695 | del self._id_dict[id] 696 | except KeyError: 697 | pass 698 | 699 | def on_answer(self): 700 | co = self.handle_response() 701 | fu = asyncio.ensure_future(co) 702 | fu.add_done_callback(lambda fu: fu.result()) 703 | 704 | async def handle_response(self): 705 | try: 706 | ans = await self.recv_data() 707 | except (ConnectionClosedError, ConnectionResetError): 708 | self.connect() 709 | return 710 | 711 | # recv_data (sock_recv) will overwrite the reader 712 | self.watch_answers() 713 | try: 714 | dns = dnslib.DNSRecord.parse(ans) 715 | except dnslib.dns.DNSError: 716 | logger.exception('%s: DNS parse error for %r', self, ans) 717 | return 718 | 719 | try: 720 | fu = self._id_dict[dns.header.id] 721 | except KeyError: 722 | # drop unexpected answers 723 | logger.debug('%s: dropping not-requested ans:\n%s', self, dns) 724 | return 725 | 726 | if self.on_pollution != 'noaction' and is_polluted_answer(dns, requireAR=self.requireAR): 727 | if self.on_pollution == 'skip': 728 | logger.warning('%s: skipping polluted answer:\n%s', self, dns.rr) 729 | return 730 | elif self.on_pollution == 'switch': 731 | logger.warning('%s: bad answer for %s. switching to next upstream.', 732 | self, dns.rr) 733 | fu.set_exception(SwitchServer('bad answer')) 734 | return 735 | elif self.china_only and not is_china_ip(dns) and not general_ip(dns): 736 | logger.debug('%s: non-china IPs for %s. switching to next upstream.', 737 | self, dns.rr) 738 | fu.set_exception(NotChina) 739 | return 740 | elif self.china_only and dns.q.qtype == 1 and dns.rr and all(r.rtype == 5 for r in dns.rr): 741 | # qtype=A and all rtype=CNAME 742 | logger.warning('%s: all-CNAME answer for %r, switching to next.', self, dns.q) 743 | fu.set_exception(SwitchServer('bad answer')) 744 | return 745 | fu.set_result((ans, dns)) 746 | 747 | def log_stat(self): 748 | logger.info('%s: count/avg/mdev/win/timeout/failed: %d/%.1f/%.1f/%d/%d/%d', 749 | self, 750 | self._stat.n, self._stat.avg, self._stat.mdev, 751 | self.wins, self.timeouts, self.failed_times, 752 | ) 753 | 754 | class UDPDNSClient(DNSClient): 755 | def prepare_request(self, request): 756 | return request.pack() 757 | 758 | def before_query(self): 759 | if not self._connected: 760 | self.connect() 761 | 762 | async def recv_data(self): 763 | ans = await self.loop.sock_recv(self.sock, 40960) 764 | return ans 765 | 766 | class TCPDNSClient(DNSClient): 767 | def prepare_request(self, request): 768 | p = request.pack() 769 | return struct.pack('!H', len(p)) + p 770 | 771 | def connect(self): 772 | pass 773 | 774 | def watch_answers(self): 775 | pass 776 | 777 | def before_query(self): 778 | super().connect() 779 | 780 | def connected(self, fu): 781 | fu.result() 782 | logger.debug('%s: connected', self) 783 | super().watch_answers() 784 | 785 | async def recv_data(self): 786 | s = self.sock 787 | header = await recv_exactly(s, 2) 788 | n = struct.unpack('!H', header)[0] 789 | ans = await recv_exactly(s, n) 790 | self.close() 791 | return ans 792 | 793 | def show_stats(statables, signo=0, frame=None): 794 | for s in statables: 795 | s.log_stat() 796 | 797 | def main(): 798 | try: 799 | import setproctitle 800 | setproctitle.setproctitle('beautifuldnsd') 801 | del setproctitle 802 | except ImportError: 803 | pass 804 | 805 | import argparse 806 | parser = argparse.ArgumentParser(description='Quick and clean DNS Proxy') 807 | parser.add_argument('-c', '--config', default='/etc/beautifuldnsd.yaml', 808 | type=argparse.FileType(), 809 | help='path to configuration file') 810 | parser.add_argument('-d', '--db-dir', 811 | help='database directory, overriding configuration file') 812 | args = parser.parse_args() 813 | 814 | global BOGUS_IPS, POLLUTION_IPS, POLLUTED_DOMAINS, BOGUS_IPS_DB, GEOIP 815 | config = loadyaml(args.config) 816 | args.config.close() 817 | BOGUS_IPS = set(config['bogus_ips']) 818 | POLLUTION_IPS = [ipaddress.ip_network(x) 819 | for x in config['pollution_ips']] 820 | concurrent_delay = config['misc']['concurrent_delay'] 821 | if pygeoip and config['misc'].get('geoip_db', False): 822 | GEOIP = pygeoip.GeoIP(config['misc']['geoip_db']) 823 | 824 | logging.getLogger('asyncio').setLevel(logging.WARN) 825 | level = config['misc']['logging_level'].upper() 826 | config_logging(level=level) 827 | 828 | db_dir = args.db_dir or config['misc']['db_directory'] 829 | POLLUTED_DOMAINS = dbm.open(os.path.join(db_dir, 'polluted_domains.db'), 'c') 830 | BOGUS_IPS_DB = shelve.open(os.path.join(db_dir, 'bogus_ips.db'), 'c') 831 | now = time.time() 832 | for ip, last in tuple(BOGUS_IPS_DB.items()): 833 | if now - last < 7 * 24 * 3600: 834 | BOGUS_IPS.add(ip) 835 | else: 836 | del BOGUS_IPS_DB[ip] 837 | logger.info('bogus IPs: %r', BOGUS_IPS) 838 | 839 | upstreams = [DNSClient(**x) for x in config['upstreams']] 840 | servers = [ 841 | UDPDNSServer( 842 | (s['ip'], s['port']), 843 | upstreams, 844 | concurrent_delay = concurrent_delay, 845 | ) for s in config['servers']] 846 | 847 | loop = asyncio.get_event_loop() 848 | my_show_stats = partial(show_stats, servers + upstreams) 849 | signal.signal(signal.SIGUSR1, my_show_stats) 850 | logging.info('serving on %r.', servers) 851 | try: 852 | loop.run_forever() 853 | finally: 854 | my_show_stats() 855 | BOGUS_IPS_DB.close() 856 | POLLUTED_DOMAINS.close() 857 | 858 | if __name__ == '__main__': 859 | import tracemalloc 860 | tracemalloc.start(25) 861 | 862 | try: 863 | main() 864 | except KeyboardInterrupt: 865 | pass 866 | 867 | snapshot = tracemalloc.take_snapshot() 868 | top_stats = snapshot.statistics('lineno') 869 | print("[ Top 10 ]") 870 | for stat in top_stats[:10]: 871 | print(stat) 872 | -------------------------------------------------------------------------------- /beautifuldnsd.yaml: -------------------------------------------------------------------------------- 1 | servers: 2 | # 监听的 IP。如果要给局域网内其它机器提供服务请设置为 0.0.0.0 3 | - ip: 127.0.0.1 4 | # 监听的端口号。配合 dnsmasq 使用时,将这里与 dnsmasq 中的设置保持一致即可 5 | port: 53535 6 | 7 | # 上游 DNS 服务器的配置 8 | upstreams: 9 | # 第一个推荐使用 ISP 提供的 DNS 服务器地址 10 | - ip: 202.106.0.20 11 | port: 53 12 | # 遇到 DNS 污染的处理。默认跳过(skip)。国内 DNS 服务器请设置为 switch 切换到其它 13 | # 如果是经过代理的不受污染的服务器,请设置为 noaction 14 | # DNS 服务器 15 | on_pollution: switch 16 | china_only: true 17 | # 是否自动发现针对不存在域名的假的应答。ISP DNS 服务器请设置为 true 18 | discover_bogus_ip: true 19 | # 过多久重新发现一次 20 | discover_interval: 600 # seconds 21 | - ip: 8.8.8.8 22 | on_pollution: skip 23 | # 设置 socks5 代理 24 | # proxy: 25 | # - 127.0.0.1 26 | # - 1080 27 | # 给数据包打上标签以便路由 28 | # mark: 1 29 | # 接受 ESNI 请求(建议只对私密的服务器启用) 30 | # accept_esni: true 31 | # 路由器提供的 DNS 功能 32 | - ip: 192.168.1.1 33 | on_pollution: switch 34 | discover_bogus_ip: true 35 | china_only: true 36 | - ip: 4.2.2.2 37 | on_pollution: skip 38 | 39 | # 这些是各个字段的默认值: 40 | # ip: required 41 | # port: 53 42 | # proto: udp 43 | # timeout: 10 # seconds 44 | # on_pollution: skip 45 | # china_only: false 46 | # accept_esni: false 47 | # discover_bogus_ip: false 48 | # discover_interval: 600 # seconds 49 | 50 | misc: 51 | # 并发延迟。在经过多少秒之后若还没有收到应答就向下一个服务器发起请求 52 | concurrent_delay: 0.15 # seconds 53 | logging_level: info 54 | # 用于存储数据的目录。需要写权限。可以使用命令行参数覆盖。 55 | db_directory: db 56 | geoip_db: /usr/share/GeoIP/GeoIP.dat 57 | 58 | # 过滤掉 DNS 污染 59 | # 由于最近一次的 DNS 污染升级,这里列出的 IP 已不完整,已经无法完全防止 DNS 污 60 | # 染。参见: https://www.v2ex.com/t/158318 61 | pollution_ips: 62 | - 159.106.121.75 63 | - 159.24.3.173 64 | - 202.106.1.2 65 | - 202.181.7.85 66 | - 203.161.230.171 67 | - 203.98.7.65 68 | - 209.145.54.50 69 | - 216.234.179.13 70 | - 243.185.187.39 71 | - 37.61.54.158 72 | - 4.36.66.178 73 | - 46.82.174.68 74 | - 59.24.3.173 75 | - 64.33.88.161 76 | - 78.16.49.15 77 | - 8.7.198.45 78 | - 93.46.8.89 79 | 80 | - 1.1.127.45 81 | - 1.1.67.51 82 | - 1.2.3.4 83 | - 1.209.208.200 84 | - 1.226.83.147 85 | - 1.234.21.83 86 | - 1.234.29.40 87 | - 1.234.39.14 88 | - 1.234.4.91 89 | - 1.234.70.80 90 | - 1.234.83.104 91 | - 1.244.115.172 92 | - 1.33.170.68 93 | - 1.33.188.62 94 | - 1.33.190.228 95 | - 1.33.190.70 96 | - 1.33.191.58 97 | - 104.200.31.226 98 | - 104.28.1.22 99 | - 104.28.14.112 100 | - 104.28.20.14 101 | - 104.28.30.59 102 | - 106.186.120.157 103 | - 106.187.39.80 104 | - 107.6.34.101 105 | - 108.168.215.230 106 | - 108.168.250.3 107 | - 108.179.196.77 108 | - 108.179.250.106 109 | - 108.61.250.218 110 | - 109.123.115.205 111 | - 109.206.173.212 112 | - 109.234.159.38 113 | - 109.71.81.130 114 | - 110.173.154.142 115 | - 110.74.163.40 116 | - 112.175.60.31 117 | - 113.11.194.190 118 | - 113.160.102.90 119 | - 118.145.17.184 120 | - 118.219.253.245 121 | - 118.5.49.6 122 | - 119.18.62.130 123 | - 119.235.57.82 124 | - 119.245.217.155 125 | - 119.9.94.83 126 | - 12.87.133.0 127 | - 120.89.93.248 128 | - 122.214.2.171 129 | - 122.218.101.190 130 | - 123.126.249.238 131 | - 123.30.175.29 132 | - 123.50.49.171 133 | - 125.230.148.48 134 | - 127.0.0.2 135 | - 128.121.126.139 136 | - 128.199.180.162 137 | - 133.192.181.66 138 | - 133.242.165.24 139 | - 133.42.48.3 140 | - 137.135.129.175 141 | - 14.102.249.18 142 | - 141.101.118.102 143 | - 141.8.195.47 144 | - 141.8.195.78 145 | - 141.8.225.80 146 | - 142.4.5.109 147 | - 144.76.106.232 148 | - 144.76.127.114 149 | - 144.76.21.13 150 | - 145.253.183.23 151 | - 147.87.244.32 152 | - 155.92.182.118 153 | - 157.205.32.64 154 | - 157.7.143.209 155 | - 159.106.121.75 156 | - 159.253.20.179 157 | - 159.50.88.77 158 | - 16.63.155.0 159 | - 162.159.243.101 160 | - 162.243.137.163 161 | - 162.253.33.134 162 | - 164.109.96.232 163 | - 164.138.221.68 164 | - 168.156.168.21 165 | - 169.132.13.103 166 | - 171.17.130.53 167 | - 171.25.204.141 168 | - 173.192.219.59 169 | - 173.194.127.144 170 | - 173.201.216.6 171 | - 173.224.209.14 172 | - 173.236.228.108 173 | - 173.244.184.10 174 | - 173.255.194.174 175 | - 173.255.230.196 176 | - 174.142.113.142 177 | - 174.142.22.25 178 | - 176.10.37.81 179 | - 176.57.216.145 180 | - 178.18.82.216 181 | - 178.236.177.77 182 | - 178.32.111.136 183 | - 178.32.156.59 184 | - 178.32.247.82 185 | - 178.33.212.162 186 | - 178.49.132.135 187 | - 178.62.242.156 188 | - 178.62.75.99 189 | - 178.79.182.248 190 | - 180.153.225.168 191 | - 180.179.171.121 192 | - 180.87.182.227 193 | - 181.224.155.41 194 | - 183.111.141.95 195 | - 184.154.10.146 196 | - 184.169.132.244 197 | - 184.72.253.232 198 | - 185.25.150.45 199 | - 185.53.61.50 200 | - 188.132.250.186 201 | - 188.165.31.24 202 | - 188.226.207.251 203 | - 188.40.108.13 204 | - 188.5.4.96 205 | - 189.163.17.5 206 | - 192.104.44.6 207 | - 192.121.151.106 208 | - 192.67.198.6 209 | - 192.95.98.202 210 | - 193.105.145.158 211 | - 193.169.66.88 212 | - 193.203.48.18 213 | - 193.234.233.149 214 | - 193.238.151.98 215 | - 193.239.132.44 216 | - 193.48.96.218 217 | - 193.57.244.117 218 | - 193.91.26.132 219 | - 194.149.250.20 220 | - 194.185.115.1 221 | - 194.187.94.6 222 | - 194.67.144.70 223 | - 195.146.235.33 224 | - 195.149.210.211 225 | - 195.154.243.151 226 | - 195.191.149.103 227 | - 195.2.88.68 228 | - 195.211.72.200 229 | - 195.43.82.170 230 | - 195.49.201.30 231 | - 195.50.195.15 232 | - 195.74.38.62 233 | - 195.74.78.21 234 | - 195.77.241.242 235 | - 195.8.125.64 236 | - 197.4.4.12 237 | - 198.143.143.36 238 | - 198.57.205.133 239 | - 198.57.222.88 240 | - 198.58.124.68 241 | - 199.167.31.142 242 | - 199.21.68.222 243 | - 199.79.63.83 244 | - 2.1.1.2 245 | - 2.187.253.121 246 | - 2.228.123.7 247 | - 2.228.154.8 248 | - 20.139.56.0 249 | - 200.229.206.115 250 | - 200.98.234.14 251 | - 201.77.211.143 252 | - 202.106.1.2 253 | - 202.181.7.85 254 | - 202.218.219.10 255 | - 202.6.96.25 256 | - 203.113.173.22 257 | - 203.133.238.172 258 | - 203.161.230.171 259 | - 203.199.57.81 260 | - 203.98.7.65 261 | - 206.108.51.91 262 | - 206.113.150.70 263 | - 207.12.88.98 264 | - 207.126.59.27 265 | - 207.140.149.247 266 | - 207.58.177.166 267 | - 208.109.138.55 268 | - 208.109.205.232 269 | - 208.112.102.122 270 | - 208.43.134.107 271 | - 208.43.33.194 272 | - 208.56.31.43 273 | - 208.73.211.164 274 | - 208.86.154.112 275 | - 208.93.0.150 276 | - 209.116.71.109 277 | - 209.126.106.182 278 | - 209.141.48.35 279 | - 209.145.54.50 280 | - 209.188.7.186 281 | - 209.204.148.22 282 | - 209.220.30.174 283 | - 209.235.224.25 284 | - 209.36.73.33 285 | - 209.43.1.130 286 | - 209.56.158.42 287 | - 209.62.154.94 288 | - 209.85.229.138 289 | - 210.175.255.154 290 | - 210.209.110.199 291 | - 210.230.192.183 292 | - 211.43.203.33 293 | - 211.5.133.18 294 | - 211.8.69.27 295 | - 211.94.66.147 296 | - 212.227.98.130 297 | - 212.45.52.219 298 | - 212.68.42.67 299 | - 212.77.104.29 300 | - 213.108.66.21 301 | - 213.133.111.102 302 | - 213.169.251.35 303 | - 213.174.158.108 304 | - 213.186.33.5 305 | - 213.19.161.141 306 | - 213.207.85.148 307 | - 213.238.166.227 308 | - 216.12.205.2 309 | - 216.139.213.144 310 | - 216.178.241.101 311 | - 216.198.246.103 312 | - 216.221.188.182 313 | - 216.234.179.13 314 | - 216.250.115.144 315 | - 216.38.0.92 316 | - 216.70.88.29 317 | - 216.92.58.37 318 | - 217.160.42.85 319 | - 217.172.183.9 320 | - 217.30.184.161 321 | - 218.44.251.212 322 | - 220.110.150.90 323 | - 220.247.224.8 324 | - 221.213.49.149 325 | - 221.8.69.27 326 | - 222.122.56.219 327 | - 23.23.14.192 328 | - 23.89.5.60 329 | - 24.51.184.0 330 | - 243.185.187.30 331 | - 243.185.187.39 332 | - 249.129.46.48 333 | - 253.157.14.165 334 | - 28.121.126.139 335 | - 28.13.216.0 336 | - 31.169.90.4 337 | - 31.170.8.8 338 | - 31.210.156.212 339 | - 31.22.4.60 340 | - 31.222.185.202 341 | - 31.25.191.134 342 | - 34.254.247.151 343 | - 37.1.205.21 344 | - 37.1.207.129 345 | - 37.140.238.35 346 | - 37.187.134.150 347 | - 37.187.149.129 348 | - 37.187.251.35 349 | - 37.252.122.184 350 | - 37.58.78.79 351 | - 37.59.25.95 352 | - 37.61.54.158 353 | - 37.99.194.148 354 | - 38.117.98.231 355 | - 4.17.143.131 356 | - 4.193.80.0 357 | - 4.21.70.9 358 | - 4.30.13.168 359 | - 4.30.187.9 360 | - 4.30.235.229 361 | - 4.31.139.146 362 | - 4.34.180.178 363 | - 4.35.100.20 364 | - 4.35.234.200 365 | - 4.36.66.178 366 | - 4.53.17.215 367 | - 4.59.79.206 368 | - 4.78.167.196 369 | - 4.79.129.122 370 | - 41.79.20.9 371 | - 43.253.199.12 372 | - 46.137.219.7 373 | - 46.165.231.144 374 | - 46.20.126.252 375 | - 46.20.13.100 376 | - 46.229.175.95 377 | - 46.243.6.170 378 | - 46.30.212.198 379 | - 46.38.24.209 380 | - 46.82.174.68 381 | - 49.2.123.56 382 | - 49.212.153.128 383 | - 5.10.105.41 384 | - 5.10.68.187 385 | - 5.10.68.188 386 | - 5.10.69.29 387 | - 5.10.77.72 388 | - 5.100.152.24 389 | - 5.100.225.204 390 | - 5.100.228.206 391 | - 5.100.231.27 392 | - 5.100.248.208 393 | - 5.144.129.20 394 | - 5.35.251.108 395 | - 5.9.118.111 396 | - 5.9.120.140 397 | - 5.9.136.210 398 | - 5.9.242.232 399 | - 5.9.5.26 400 | - 5.9.65.105 401 | - 50.116.6.162 402 | - 50.18.183.233 403 | - 50.57.11.12 404 | - 50.63.202.13 405 | - 50.87.148.140 406 | - 50.87.169.77 407 | - 50.93.207.101 408 | - 50.97.134.91 409 | - 54.174.40.182 410 | - 54.187.136.30 411 | - 54.187.39.38 412 | - 54.191.193.138 413 | - 54.200.3.32 414 | - 54.206.98.127 415 | - 54.209.238.28 416 | - 54.209.87.186 417 | - 54.218.38.198 418 | - 54.229.147.183 419 | - 54.235.199.154 420 | - 54.244.22.77 421 | - 54.246.169.32 422 | - 54.246.202.250 423 | - 54.68.166.130 424 | - 54.76.135.1 425 | - 54.83.51.191 426 | - 54.86.21.64 427 | - 54.86.223.202 428 | - 54.88.252.91 429 | - 59.124.74.28 430 | - 59.24.3.173 431 | - 61.54.28.6 432 | - 62.138.115.35 433 | - 62.75.221.31 434 | - 62.92.17.213 435 | - 64.14.72.41 436 | - 64.150.184.98 437 | - 64.22.110.34 438 | - 64.33.88.161 439 | - 64.33.99.47 440 | - 64.34.161.142 441 | - 64.50.179.133 442 | - 64.66.163.251 443 | - 64.79.69.250 444 | - 64.79.84.141 445 | - 64.91.254.97 446 | - 65.104.202.252 447 | - 65.160.219.113 448 | - 65.183.39.139 449 | - 66.146.2.241 450 | - 66.187.204.50 451 | - 66.206.11.194 452 | - 66.39.61.161 453 | - 66.45.252.237 454 | - 66.55.151.148 455 | - 66.85.134.186 456 | - 66.96.147.160 457 | - 67.137.227.11 458 | - 67.225.220.248 459 | - 68.71.58.18 460 | - 69.16.196.113 461 | - 69.167.172.162 462 | - 69.171.13.49 463 | - 69.174.244.221 464 | - 69.175.75.202 465 | - 69.195.124.90 466 | - 69.30.23.10 467 | - 69.50.192.218 468 | - 69.61.60.122 469 | - 70.42.243.33 470 | - 72.14.205.104 471 | - 72.14.205.99 472 | - 72.167.32.10 473 | - 72.20.110.50 474 | - 72.29.94.240 475 | - 72.32.4.243 476 | - 72.47.228.79 477 | - 72.5.1.109 478 | - 72.52.244.56 479 | - 74.117.117.122 480 | - 74.117.57.138 481 | - 74.124.195.73 482 | - 74.125.127.102 483 | - 74.125.155.102 484 | - 74.125.204.121 485 | - 74.125.39.102 486 | - 74.125.39.113 487 | - 74.207.236.174 488 | - 74.208.125.184 489 | - 74.220.215.67 490 | - 74.82.166.166 491 | - 75.98.175.166 492 | - 76.164.217.116 493 | - 77.4.7.92 494 | - 78.108.178.26 495 | - 78.140.172.33 496 | - 78.16.49.15 497 | - 78.24.135.99 498 | - 79.127.127.68 499 | - 79.136.125.49 500 | - 79.98.34.60 501 | - 8.105.84.0 502 | - 8.34.161.150 503 | - 8.7.198.45 504 | - 80.190.96.26 505 | - 80.241.209.19 506 | - 80.241.92.180 507 | - 80.245.171.70 508 | - 80.70.184.118 509 | - 80.72.41.146 510 | - 80.82.117.209 511 | - 80.82.201.154 512 | - 80.92.117.132 513 | - 82.145.47.117 514 | - 83.125.118.122 515 | - 83.222.124.187 516 | - 83.222.5.171 517 | - 84.124.59.165 518 | - 85.111.18.138 519 | - 85.190.0.110 520 | - 85.25.171.103 521 | - 85.92.134.229 522 | - 87.106.57.209 523 | - 87.230.46.50 524 | - 88.198.69.101 525 | - 88.214.195.67 526 | - 89.108.118.129 527 | - 89.111.181.74 528 | - 89.186.95.11 529 | - 89.30.125.204 530 | - 89.31.55.106 531 | - 90.156.201.42 532 | - 91.121.245.154 533 | - 91.186.28.41 534 | - 91.198.129.47 535 | - 91.217.73.22 536 | - 91.221.37.35 537 | - 91.223.175.25 538 | - 91.238.30.54 539 | - 91.239.201.16 540 | - 92.53.106.175 541 | - 92.53.96.9 542 | - 92.63.110.174 543 | - 93.115.240.148 544 | - 93.158.121.72 545 | - 93.187.205.2 546 | - 93.46.8.89 547 | - 93.93.187.49 548 | - 94.136.188.30 549 | - 94.141.31.140 550 | - 94.23.147.142 551 | - 94.23.156.11 552 | - 94.23.193.224 553 | - 94.23.199.144 554 | - 95.163.95.47 555 | - 95.211.150.70 556 | - 95.211.229.156 557 | - 95.211.58.97 558 | - 95.85.22.163 559 | - 96.126.97.15 560 | - 96.127.172.221 561 | - 96.30.51.148 562 | - 97.74.80.22 563 | - 98.129.229.202 564 | - 98.158.152.159 565 | - 98.158.178.141 566 | 567 | # 手动指定一些虚假的 IP 应答。通常无必要。格式同上,空的话才需要写成「[]」 568 | bogus_ips: 569 | [] 570 | -------------------------------------------------------------------------------- /systemd/beautifuldnsd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Quick and clean DNS Proxy 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/beautifuldnsd -d /var/lib/beautifuldnsd 8 | KillSignal=SIGINT 9 | User=beautifuldnsd 10 | Group=beautifuldnsd 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /systemd/tmpfiles.conf: -------------------------------------------------------------------------------- 1 | d /run/beautifuldnsd 755 beautifuldnsd beautifuldnsd - 2 | d /var/lib/beautifuldnsd 755 beautifuldnsd beautifuldnsd - 3 | --------------------------------------------------------------------------------