compassd / dcompass

A high-performance programmable DNS component aiming at robustness, speed, and flexibility
GNU General Public License v3.0
281 stars 23 forks source link
counter-censorship dns dns-over-https dns-server routing script

dcompass

Automated build Join telegram channel
A high-performance programmable DNS component.
中文版(未更新)

Features

Notice

[2022-09-19] More efficient and robust scripting with rune
Introducing dcompass v0.3.0. With the rune script engine, dcompass is about 2-6x faster than the previous version with unparalleled concurrency stability. However, existing configurations are no longer valid. Please see example configs to migrate.

[2022-06-22] All-new script engine
Introducing dcompass v0.2.0. With the new script engine, you can now access every bit, every record, and every section of every DNS message. Program dcompass into whatever you want! However, existing configurations are no longer valid. Please see examples to migrate.

[2021-9-16] Expression Engine and breaking changes
dcompass is now equipped with an expression engine which let you easily and freely compose logical expressions with existing matchers. This enables us to greatly improve config readablity and versatility. However, all existing config files involving if rule block are no longer working. Please see examples to migrate.

[2021-07-28] 2x faster and breaking changes
We adopted a brand new bare metal DNS library domain which allows us to manipulate DNS messages without much allocation. This adoption significantly improves the memory footprint and throughput of dcompass. Due to this major refactorization, DoT/TCP/zone protocol are temporarily unavailable, however, UDP and DoH connections are now blazing fast. We will gradually put back those protocols.

Usages

dcompass -c path/to/config.json # Or YAML

Or you can simply run dcompass from the folder where your configuration file named config.yml resides.
You can also validate your configuration

dcompass -c path/to/config.json -v

Quickstart

See example.yaml

Below is a script using GeoIP to mitigate DNS pollution

script: |
  pub async fn route(upstreams, inited, ctx, query) {
    let resp = upstreams.send_default("domestic", query).await?;

    for ans in resp.answer? {
      match ans.rtype.to_str() {
        "A" if !inited.geoip.0.contains(ans.to_a()?.ip, "CN") => { return upstreams.send_default("secure", query).await; }
        "AAAA" if !inited.geoip.0.contains(ans.to_aaaa()?.ip, "CN") => { return upstreams.send_default("secure", query).await; }
        _ => continue,
      }
    }
    Ok(resp)
  }

  pub async fn init() {
    Ok(#{"geoip": Utils::GeoIp(GeoIp::create_default()?)})
  }

And another script that adds EDNS Client Subnet record into the OPT pseudo-section:

script: |
  pub async fn route(upstreams, inited, ctx, query) {
    // Optionally remove all the existing OPT pseudo-section(s)
    // query.clear_opt();

    query.push_opt(ClientSubnet::new(u8(15), u8(0), IpAddr::from_str("23.62.93.233")?).to_opt_data())?;

    upstreams.send_default("ali", query).await
  }

Or implement your simple xip.io service:

script: |
  pub async fn route(upstreams, inited, ctx, query) {
    let header = query.header;
    header.qr = true;
    query.header = header;

    let ip_str = query.first_question?.qname.to_str();
    let ip = IpAddr::from_str(ip_str.replace(".xip.io", ""))?;

    query.push_answer(DnsRecord::new(query.first_question?.qname, Class::from_str("IN")?, 3600, A::new(ip)?.to_rdata()))?;

    Ok(query)
  }

Configuration

Configuration file contains different fields:

Different utilities:

Geo IP matcher:

IP CIDR matcher:

Domain matcher:

Different querying methods:

See example.yaml for a pre-configured out-of-box anti-pollution configuration (Only works with full or cn version, to use with min, please provide your own database).

Packages

You can download binaries at release page.

  1. GitHub Action build is set up x86_64, i686, arm, and mips. Check them out on release page!
  2. NixOS package is available at this repo as a flake. Also, for NixOS users, a NixOS modules is provided with systemd services and easy-to-setup interfaces in the same repository where package is provided.
└───packages
    ├───aarch64-linux
    │   ├───dcompass-cn: package 'dcompass-cn-git'
    │   └───dcompass-maxmind: package 'dcompass-maxmind-git'
    ├───i686-linux
    │   ├───dcompass-cn: package 'dcompass-cn-git'
    │   └───dcompass-maxmind: package 'dcompass-maxmind-git'
    ├───x86_64-darwin
    │   ├───dcompass-cn: package 'dcompass-cn-git'
    │   └───dcompass-maxmind: package 'dcompass-maxmind-git'
    └───x86_64-linux
        ├───dcompass-cn: package 'dcompass-cn-git'
        └───dcompass-maxmind: package 'dcompass-maxmind-git'

cache is available at cachix, with public key dcompass.cachix.org-1:uajJEJ1U9uy/y260jBIGgDwlyLqfL1sD5yaV/uWVlbk= (outputs.publicKey).

Benchmark

Mocked benchmark (server served on local loopback):

Gnuplot not found, using plotters backend
non_cache_resolve       time:   [20.548 us 20.883 us 21.282 us]
                        change: [-33.128% -30.416% -27.511%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  6 (6.00%) high mild
  5 (5.00%) high severe

cached_resolve          time:   [2.6429 us 2.6493 us 2.6566 us]
                        change: [-90.684% -90.585% -90.468%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe

TODO-list

License

All three components dmatcher, droute, dcompass are licensed under GPLv3+. dcompass with geoip feature gate enabled includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com.