import logging import subprocess from urllib.request import urlopen from dnscrypt import parse, DNSoverHTTPS SMARTDNS_GFW_CONF_FILE = '/etc/smartdns/conf.d/gfw.conf' # https://github.com/DNSCrypt/dnscrypt-resolvers PUBLIC_RESOLVER_URL_LIST = [ "https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md", "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md" "https://dnsr.evilvibes.com/v3/public-resolvers.md" ] def get_public_resolver_md() -> str: for url in PUBLIC_RESOLVER_URL_LIST: try: logging.info('request {url}'.format(url=url)) with urlopen(url, timeout=15) as responsee: return responsee.read().decode('utf-8') except: pass raise IOError("can't download public-resolvers.md") def get_stamps(): resolver_md: str = get_public_resolver_md() lines = resolver_md.splitlines() stamps = list( map( parse, filter(lambda x: x.startswith('sdns://'), lines) ) ) return stamps def get_not_china_doh_list(): stamps = get_stamps() def is_match(s) -> bool: if isinstance(s, DNSoverHTTPS) is False: return False if s.nofilter is False: return False if s.dnssec is False: return False return True return list(filter( is_match, stamps )) def get_smartdns_config(): stamps = get_not_china_doh_list() lines = set(map( lambda x: 'server-https https://' + x.hostname + x.path + ' -group GFW -exclude-default-group', stamps )) return '\n'.join(lines) def write_smartdns_config(): conf_txt = get_smartdns_config() with open(SMARTDNS_GFW_CONF_FILE, 'w') as f: f.write(conf_txt) def reload_smartdns(): subprocess.run(["/etc/init.d/smartdns", "reload"]) def main(): write_smartdns_config() reload_smartdns() if __name__ == '__main__': main()