Skip to content

Commit

Permalink
🎨 impl reverse IP address mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
mokeyish committed Dec 23, 2022
1 parent 0647da4 commit f90ab9b
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 107 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ service-manager = { version = "0.2.0", git = "https://github.com/chipsenkbeil/se
hostname ={ version = "*", git = "https://github.com/mokeyish/hostname.git", branch = "dev"}
byte-unit = "4.0.17"
ipnet = "2.7"
local-ip-address = "0.4.9"
# rnp = "0.1"
# boomphf = "0.5.9"

Expand Down
38 changes: 10 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SmartDNS-rs

[English](https://github.com/mokeyish/smartdns-rs/blob/master/README_en-US.md) | 中文
[English](https://github.com/mokeyish/smartdns-rs/blob/main/README_en-US.md) | 中文

SmartDNS-rs 🐋 一个是受 [C 语言版 smartdns](https://github.com/pymumu/smartdns) 启发而开发的,并与其配置兼容的运行在本地的跨平台 DNS 服务器,
它接受来自本地客户端的 DNS 查询请求,然后从多个上游 DNS 服务器获取 DNS 查询结果,并将访问速度最快的结果返回给客户端,
Expand All @@ -13,7 +13,6 @@ SmartDNS-rs 🐋 一个是受 [C 语言版 smartdns](https://github.com/pymumu/s
**目前正在开发中,请勿用于生产环境。**



## 构建与运行

打开的你的命令行界面,执行如下命令:
Expand Down Expand Up @@ -44,35 +43,19 @@ sudo ./target/release/smartdns run -c ./etc/smartdns/smartdns.conf
./smartdns service help
```

### MacOS

1. 安装服务

```shell
sudo ./smartdns service install
```

2. 启动服务

```shell
sudo ./smartdns service start
```

3. 关闭服务

```shell
sudo ./smartdns service stop
```
- MacOS
- [x] launchctl
- Windows
- [x] Sc - [https://learn.microsoft.com/en-us/sc](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc754599(v=ws.11))
- Linux
- [x] Systemd - https://en.wikipedia.org/wiki/Systemd
- [ ] OpenRc - https://en.wikipedia.org/wiki/OpenRC
- [ ] Procd(OpenWrt) - https://openwrt.org/docs/techref/procd

4. 卸载服务

```shell
sudo ./smartdns service uninstall
```

### Linux
### Linux / MacOS

暂时支持 `systemd`.

1. 安装服务

Expand Down Expand Up @@ -127,7 +110,6 @@ sudo ./target/release/smartdns run -c ./etc/smartdns/smartdns.conf
```



## 配置文件说明

功能覆盖状态(更多详细的配置请参考 [这里](https://github.com/pymumu/smartdns#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E)
Expand Down
49 changes: 15 additions & 34 deletions README_en-US.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SmartDNS-rs

English | [中文](https://github.com/mokeyish/smartdns-rs/blob/master/README.md)
English | [中文](https://github.com/mokeyish/smartdns-rs/blob/main/README.md)

A local DNS server imspired by [c smartdns](https://github.com/pymumu/smartdns) to accepts DNS query requests from local clients, obtains DNS query results from multiple upstream DNS servers, and returns the fastest access results to clients.
Avoiding DNS pollution and improving network access speed, supports high-performance ad filtering.
Expand All @@ -11,10 +11,6 @@ Note: The c version of smartdns is very functional, but because it only supports

**Currently under development, please do not use in production environment.**

## Configuration parameter

Please refer to [here](https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter) for configuration.

## Building

Open your terminal and execute these commands:
Expand Down Expand Up @@ -45,35 +41,16 @@ You can use the following command to view the help of service management command
./smartdns service help
```

### MacOS

1. Install service

```shell
sudo ./smartdns service install
```

2. Start service

```shell
sudo ./smartdns service start
```

3. Stop service
- MacOS
- [x] launchctl
- Windows
- [x] Sc - [https://learn.microsoft.com/en-us/sc](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc754599(v=ws.11))
- Linux
- [x] Systemd - https://en.wikipedia.org/wiki/Systemd
- [ ] OpenRc - https://en.wikipedia.org/wiki/OpenRC
- [ ] Procd(OpenWrt) - https://openwrt.org/docs/techref/procd

```shell
sudo ./smartdns service stop
```

4. Uninstall service

```shell
sudo ./smartdns service uninstall
```

### Linux

Currently only support `systemd`.
### Linux / MacOS

1. Install service

Expand All @@ -87,7 +64,7 @@ Currently only support `systemd`.
sudo ./smartdns service start
```

3. Stop Service
3. Stop service

```shell
sudo ./smartdns service stop
Expand Down Expand Up @@ -127,6 +104,10 @@ Run cmd or powershell as administrator to execute the command below.
./smartdns service uninstall
```

## Configuration parameter

Please refer to [here](https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter) for configuration.


## Others

Expand Down
68 changes: 26 additions & 42 deletions src/dns_mw_addr.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use std::str::FromStr;

use crate::dns::*;
use crate::dns_conf::SmartDnsConfig;
use crate::matcher::DomainAddressMatcher;
use crate::middleware::*;
use trust_dns_client::rr::{RData, RecordType};
use trust_dns_resolver::Name;

#[derive(Debug)]
pub struct AddressMiddleware {
Expand All @@ -28,48 +25,35 @@ impl Middleware<DnsContext, DnsRequest, DnsResponse, DnsError> for AddressMiddle
req: &DnsRequest,
next: crate::middleware::Next<'_, DnsContext, DnsRequest, DnsResponse, DnsError>,
) -> Result<DnsResponse, DnsError> {
match req.query().query_type() {
// handle AAAA and A only.
RecordType::AAAA | RecordType::A if self.map.len() > 0 => {
let name = req.query().name();
if let Some(addr) = self.map.find(name) {
let rdata = match addr {
crate::dns_conf::DomainAddress::IPv4(ipv4) => Some(RData::A(*ipv4)),
crate::dns_conf::DomainAddress::IPv6(ipv6) => Some(RData::AAAA(*ipv6)),
crate::dns_conf::DomainAddress::SOA => Some(RData::default_soa()),
crate::dns_conf::DomainAddress::SOAv4
if req.query().query_type() == RecordType::A =>
{
Some(RData::default_soa())
}
crate::dns_conf::DomainAddress::SOAv6
if req.query().query_type() == RecordType::AAAA =>
{
Some(RData::default_soa())
}
crate::dns_conf::DomainAddress::IGN => None,
crate::dns_conf::DomainAddress::IGNv4 => None,
crate::dns_conf::DomainAddress::IGNv6 => None,
_ => None,
};

if let Some(rdata) = rdata {
let lookup = Lookup::from_rdata(req.query().original().to_owned(), rdata);
ctx.lookup_source = LookupSource::Static;
return Ok(lookup);
if matches!(req.query().query_type(), RecordType::AAAA | RecordType::A) {
let name = req.query().name();
if let Some(addr) = self.map.find(name) {
let rdata = match addr {
crate::dns_conf::DomainAddress::IPv4(ipv4) => Some(RData::A(*ipv4)),
crate::dns_conf::DomainAddress::IPv6(ipv6) => Some(RData::AAAA(*ipv6)),
crate::dns_conf::DomainAddress::SOA => Some(RData::default_soa()),
crate::dns_conf::DomainAddress::SOAv4
if req.query().query_type() == RecordType::A =>
{
Some(RData::default_soa())
}
crate::dns_conf::DomainAddress::SOAv6
if req.query().query_type() == RecordType::AAAA =>
{
Some(RData::default_soa())
}
crate::dns_conf::DomainAddress::IGN => None,
crate::dns_conf::DomainAddress::IGNv4 => None,
crate::dns_conf::DomainAddress::IGNv6 => None,
_ => None,
};

if let Some(rdata) = rdata {
let lookup = Lookup::from_rdata(req.query().original().to_owned(), rdata);
ctx.lookup_source = LookupSource::Static;
return Ok(lookup);
}
}
RecordType::PTR
if req.query().name() == &Name::from_str("whoami").unwrap().into()
|| req.query().name() == &Name::from_str("smartdns").unwrap().into() =>
{
return Ok(Lookup::from_rdata(
req.query().original().to_owned(),
RData::PTR(ctx.cfg.server_name()),
));
}
_ => (),
}

next.run(ctx, req).await
Expand Down
122 changes: 121 additions & 1 deletion src/dns_mw_zone.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
use std::collections::{BTreeSet, HashMap};
use std::str::FromStr;

use trust_dns_client::rr::LowerName;
use trust_dns_server::authority::{AuthorityObject, LookupOptions};

use crate::dns::*;
use crate::dns_conf::SmartDnsConfig;
use crate::log::debug;
use crate::middleware::*;
use crate::third_ext::IpAddrToArpa;

pub struct DnsZoneMiddleware {
catalog: Catalog,
ptr_set: BTreeSet<LowerName>,
}

impl DnsZoneMiddleware {
pub fn new(_cfg: &SmartDnsConfig) -> Self {
let catalog = Catalog::new();

let ptr_set = {
let mut set = BTreeSet::<LowerName>::new();
set.insert(Name::from_str("whoami").unwrap().into());
set.insert(Name::from_str("smartdns").unwrap().into());

use local_ip_address::list_afinet_netifas;
if let Ok(network_interfaces) = list_afinet_netifas() {
for (_, ip) in network_interfaces.iter() {
if let Ok(n) = Name::from_str(ip.to_arpa().as_str()) {
set.insert(n.into());
}
}
}

pub struct DnsZoneMiddleware;
set
};

Self { catalog, ptr_set }
}
}

#[async_trait::async_trait]
impl Middleware<DnsContext, DnsRequest, DnsResponse, DnsError> for DnsZoneMiddleware {
Expand All @@ -11,6 +48,89 @@ impl Middleware<DnsContext, DnsRequest, DnsResponse, DnsError> for DnsZoneMiddle
req: &DnsRequest,
next: Next<'_, DnsContext, DnsRequest, DnsResponse, DnsError>,
) -> Result<DnsResponse, DnsError> {
let query = req.query();
let name = query.name();
let rtype = query.query_type();

if rtype == RecordType::PTR && self.ptr_set.contains(name) {
return Ok(Lookup::from_rdata(
req.query().original().to_owned(),
RData::PTR(ctx.cfg.server_name()),
));
};

if let Some(authority) = self.catalog.find(name) {
if let Ok(lookup) = authority
.lookup(name, rtype, LookupOptions::default())
.await
{
let records = lookup.iter().map(|r| r.to_owned()).collect::<Vec<_>>();
if !records.is_empty() {
return Ok(DnsResponse::new_with_max_ttl(
query.original().to_owned(),
records.into(),
));
}
}
}

next.run(ctx, req).await
}
}

struct Catalog {
authorities: HashMap<LowerName, Box<dyn AuthorityObject>>,
}

impl Catalog {
/// Constructs a new Catalog
pub fn new() -> Self {
Self {
authorities: Default::default(),
}
}

/// Insert or update a zone authority
///
/// # Arguments
///
/// * `name` - zone name, e.g. example.com.
/// * `authority` - the zone data
pub fn upsert(&mut self, name: LowerName, authority: Box<dyn AuthorityObject>) {
self.authorities.insert(name, authority);
}

/// Remove a zone from the catalog
pub fn remove(&mut self, name: &LowerName) -> Option<Box<dyn AuthorityObject>> {
self.authorities.remove(name)
}

/// Checks whether the `Catalog` contains DNS records for `name`
///
/// Use this when you know the exact `LowerName` that was used when
/// adding an authority and you don't care about the authority it
/// contains. For public domain names, `LowerName` is usually the
/// top level domain name like `example.com.`.
///
/// If you do not know the exact domain name to use or you actually
/// want to use the authority it contains, use `find` instead.
pub fn contains(&self, name: &LowerName) -> bool {
self.authorities.contains_key(name)
}

/// Recursively searches the catalog for a matching authority
pub fn find(&self, name: &LowerName) -> Option<&(dyn AuthorityObject + 'static)> {
debug!("searching authorities for: {}", name);
self.authorities
.get(name)
.map(|authority| &**authority)
.or_else(|| {
if !name.is_root() {
let name = name.base_name();
self.find(&name)
} else {
None
}
})
}
}
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ fn run_server(conf: Option<PathBuf>, debug: bool) {
));
}

middleware_builder = middleware_builder.with(DnsZoneMiddleware);
middleware_builder = middleware_builder.with(DnsZoneMiddleware::new(&cfg));

middleware_builder = middleware_builder.with(AddressMiddleware::new(&cfg));
if cfg.address_rules.len() > 0 {
middleware_builder = middleware_builder.with(AddressMiddleware::new(&cfg));
}

// check if cache enabled.
if cfg.cache_size() > 0 {
Expand Down
Loading

0 comments on commit f90ab9b

Please sign in to comment.