Skip to content

Commit 838f4f3

Browse files
committed
feat: add dockerfile and some rdata types
1 parent a3091d1 commit 838f4f3

File tree

14 files changed

+765
-167
lines changed

14 files changed

+765
-167
lines changed

.dockerignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
### Rust template
2+
# Generated by Cargo
3+
# will have compiled files and executables
4+
debug/
5+
target/
6+
7+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
8+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
9+
Cargo.lock
10+
11+
# These are backup files generated by rustfmt
12+
**/*.rs.bk
13+
14+
# MSVC Windows builds of rustc generate these, which store debugging information
15+
*.pdb
16+
17+
### Others
18+
docs/
19+
.github/
20+
justfile
21+
GeoLite2-Country.mmdb
22+
.env
23+
README.md
24+
.pre-commit-config.yaml
25+
docker-compose.yml
26+
config.toml
27+
config.*.toml

Dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM rust:1.76-alpine as builder
2+
WORKDIR /usr/src/zerodns
3+
COPY . .
4+
5+
RUN apk add --no-cache musl-dev
6+
7+
RUN cargo install --path .
8+
9+
FROM alpine:3
10+
11+
LABEL maintainer="[email protected]"
12+
13+
COPY --from=builder /usr/local/cargo/bin/zerodns /usr/local/bin/zerodns
14+
15+
ENTRYPOINT ["zerodns"]

config.toml

+20-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[server]
22
listen = "0.0.0.0:5454"
3-
cache_size = 1000
3+
# cache_size = 1000
44

55
[filters.test]
66
kind = "proxyby"
@@ -14,25 +14,27 @@ props = { servers = ["223.5.5.5", "223.6.6.6"] }
1414
kind = "proxyby"
1515
props = { servers = ["8.8.8.8", "8.8.4.4"] }
1616

17-
[filters.chinadns]
18-
kind = "chinadns"
19-
[filters.chinadns.props]
20-
trusted = ["tcp://8.8.8.8", "tcp://8.8.4.4"]
21-
mistrusted = ["223.5.5.5", "223.6.6.6"]
22-
geoip_database = "GeoLite2-Country.mmdb"
17+
[filters.tcpgoogle]
18+
kind = "proxyby"
19+
props = { servers = ["tcp://8.8.8.8", "tcp://8.8.4.4"] }
2320

24-
[[rules]]
25-
domain = "*bing*"
26-
filter = "test"
2721

28-
[[rules]]
29-
domain = "*.cn"
30-
filter = "china"
31-
32-
[[rules]]
33-
domain = "*google.com"
34-
filter = "google"
22+
[filters.chinadns]
23+
kind = "chinadns"
24+
props = { trusted = ["tcp://8.8.8.8", "tcp://8.8.4.4"], mistrusted = ["223.5.5.5", "223.6.6.6"], geoip_database = "GeoLite2-Country.mmdb" }
25+
26+
#[[rules]]
27+
#domain = "*bing*"
28+
#filter = "test"
29+
#
30+
#[[rules]]
31+
#domain = "*.cn"
32+
#filter = "china"
33+
#
34+
#[[rules]]
35+
#domain = "*google.com"
36+
#filter = "google"
3537

3638
[[rules]]
3739
domain = "*"
38-
filter = "chinadns"
40+
filter = "tcpgoogle"

docker-compose.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
services:
2+
server:
3+
build: .
4+
command:
5+
- run
6+
- -c
7+
- config.toml
8+
env_file:
9+
- .env
10+
working_dir: /app
11+
volumes:
12+
- ./GeoLite2-Country.mmdb:/app/GeoLite2-Country.mmdb
13+
- ./config.toml:/app/config.toml
14+
ports:
15+
- 15454:5454/udp
16+
- 15454:5454/tcp
17+
networks:
18+
- local
19+
20+
networks:
21+
local:
22+
external: false

src/builtin.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
use crate::filter::{register, ChinaDNSFilterFactory, Options, ProxyByFilterFactory};
1+
use crate::filter::{
2+
register, ChinaDNSFilterFactory, NoopFilter, NoopFilterFactory, Options, ProxyByFilterFactory,
3+
};
24

35
pub(crate) fn init() {
6+
register("noop", |opts: &Options| {
7+
NoopFilter::reset();
8+
Ok(NoopFilterFactory::try_from(opts).unwrap())
9+
});
410
register("proxyby", |opts: &Options| {
511
ProxyByFilterFactory::try_from(opts)
612
});

src/filter/chinadns.rs

+9-20
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::net::IpAddr;
1+
use std::net::{IpAddr, Ipv4Addr};
22
use std::sync::Arc;
33

44
use async_trait::async_trait;
55
use maxminddb::Reader;
66
use smallvec::SmallVec;
77

88
use crate::filter::misc::OptionsReader;
9-
use crate::protocol::{Kind, Message, DNS};
9+
use crate::protocol::{Kind, Message, RData, DNS};
1010
use crate::Result;
1111

1212
use super::{Context, Filter, FilterFactory, Options};
@@ -30,8 +30,8 @@ impl ChinaDNSFilter {
3030
if all_china {
3131
// reject answers of china ips
3232
for next in r.answers().filter(|it| it.kind() == Kind::A) {
33-
if let Some(addr) = next.data_as_ipaddr() {
34-
if !Self::is_china(geoip, &addr) {
33+
if let Ok(RData::A(a)) = next.rdata() {
34+
if !Self::is_china(geoip, a.ipaddr()) {
3535
return None;
3636
}
3737
}
@@ -45,9 +45,9 @@ impl ChinaDNSFilter {
4545
}
4646

4747
#[inline(always)]
48-
fn is_china(geoip: &Reader<Vec<u8>>, addr: &IpAddr) -> bool {
48+
fn is_china(geoip: &Reader<Vec<u8>>, addr: Ipv4Addr) -> bool {
4949
let mut is_china = false;
50-
if let Ok(country) = geoip.lookup::<maxminddb::geoip2::Country>(Clone::clone(addr)) {
50+
if let Ok(country) = geoip.lookup::<maxminddb::geoip2::Country>(IpAddr::V4(addr)) {
5151
if let Some(country) = country.country {
5252
is_china = matches!(country.iso_code, Some("CN"));
5353
}
@@ -178,7 +178,6 @@ impl FilterFactory for ChinaDNSFilterFactory {
178178
#[cfg(test)]
179179
mod tests {
180180
use bytes::Bytes;
181-
use smallvec::SmallVec;
182181

183182
use super::*;
184183

@@ -203,20 +202,10 @@ mod tests {
203202

204203
let show = |msg: &Message| {
205204
for next in msg.answers() {
206-
let addr = next.data_as_ipaddr();
207-
208-
let mut v = SmallVec::<[u8; 64]>::new();
209-
for (i, b) in next.name().enumerate() {
210-
if i != 0 {
211-
v.push(b'.');
212-
}
213-
v.extend_from_slice(b);
214-
}
215-
216205
info!(
217-
"answer: domain={}, address={:?}",
218-
String::from_utf8_lossy(&v[..]),
219-
&addr
206+
"answer: domain={}, rdata={}",
207+
next.name(),
208+
next.rdata().unwrap(),
220209
);
221210
}
222211
};

src/filter/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ pub(crate) use chinadns::ChinaDNSFilterFactory;
22
pub use proto::*;
33
pub(crate) use proxyby::ProxyByFilterFactory;
44
pub(crate) use registry::load;
5+
pub(crate) use registry::FilterFactoryExt;
56
pub use registry::{register, FilterFactory, Options};
67

8+
pub(crate) use noop::{NoopFilter, NoopFilterFactory};
9+
710
mod chinadns;
811
mod misc;
12+
mod noop;
913
mod proto;
1014
mod proxyby;
1115
mod registry;

src/filter/noop.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::convert::Infallible;
2+
use std::sync::atomic::{AtomicU64, Ordering};
3+
4+
use async_trait::async_trait;
5+
use once_cell::sync::Lazy;
6+
7+
use crate::filter::{Context, FilterFactory, Options};
8+
use crate::protocol::Message;
9+
10+
use super::proto::Filter;
11+
12+
static NOOP_SEQ: Lazy<(AtomicU64, AtomicU64)> =
13+
Lazy::new(|| (AtomicU64::new(0), AtomicU64::new(0)));
14+
15+
#[derive(Default)]
16+
pub(crate) struct NoopFilter {
17+
next: Option<Box<dyn Filter>>,
18+
}
19+
20+
impl NoopFilter {
21+
pub(crate) fn reset() {
22+
let (req, res) = &*NOOP_SEQ;
23+
req.store(0, Ordering::SeqCst);
24+
res.store(0, Ordering::SeqCst);
25+
}
26+
27+
pub(crate) fn requests() -> u64 {
28+
let (seq, _) = &*NOOP_SEQ;
29+
seq.load(Ordering::SeqCst)
30+
}
31+
32+
pub(crate) fn responses() -> u64 {
33+
let (_, seq) = &*NOOP_SEQ;
34+
seq.load(Ordering::SeqCst)
35+
}
36+
}
37+
38+
#[async_trait]
39+
impl Filter for NoopFilter {
40+
async fn on_request(
41+
&self,
42+
ctx: &mut Context,
43+
req: &mut Message,
44+
) -> crate::Result<Option<Message>> {
45+
let (seq, _) = &*NOOP_SEQ;
46+
47+
let cnt = seq.fetch_add(1, Ordering::SeqCst) + 1;
48+
info!("call 'on_request' from noop filter ok: cnt={}", cnt);
49+
50+
if let Some(next) = &self.next {
51+
return next.on_request(ctx, req).await;
52+
}
53+
54+
Ok(None)
55+
}
56+
57+
async fn on_response(&self, ctx: &mut Context, res: &mut Option<Message>) -> crate::Result<()> {
58+
let (_, seq) = &*NOOP_SEQ;
59+
60+
let cnt = seq.fetch_add(1, Ordering::SeqCst) + 1;
61+
info!("call 'on_response' from noop filter ok: cnt={}", cnt);
62+
63+
if let Some(next) = &self.next {
64+
return next.on_response(ctx, res).await;
65+
}
66+
67+
Ok(())
68+
}
69+
70+
fn next(&self) -> Option<&dyn Filter> {
71+
self.next.as_deref()
72+
}
73+
74+
fn set_next(&mut self, next: Box<dyn Filter>) {
75+
self.next.replace(next);
76+
}
77+
}
78+
79+
pub(crate) struct NoopFilterFactory;
80+
81+
impl FilterFactory for NoopFilterFactory {
82+
type Item = NoopFilter;
83+
84+
fn get(&self) -> crate::Result<Self::Item> {
85+
Ok(Default::default())
86+
}
87+
}
88+
89+
impl TryFrom<&Options> for NoopFilterFactory {
90+
type Error = Infallible;
91+
92+
fn try_from(value: &Options) -> Result<Self, Self::Error> {
93+
Ok(NoopFilterFactory)
94+
}
95+
}

src/handler/filtered.rs

+5-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::VecDeque;
22

33
use async_trait::async_trait;
44

5-
use crate::filter::{Context, Filter, Options};
5+
use crate::filter::{Context, Filter};
66
use crate::handler::Handler;
77
use crate::protocol::Message;
88
use crate::Result;
@@ -34,36 +34,16 @@ pub(crate) struct FilteredHandlerBuilder {
3434
}
3535

3636
impl FilteredHandlerBuilder {
37-
pub(crate) fn append<T>(self, next: T) -> Self
37+
pub(crate) fn append<T>(mut self, next: T) -> Self
3838
where
3939
T: Filter,
4040
{
41-
self.append_boxed(Box::new(next))
42-
}
43-
44-
pub(crate) fn append_with<S>(self, name: S, opts: &Options) -> Self
45-
where
46-
S: AsRef<str>,
47-
{
48-
let name = name.as_ref();
49-
match crate::filter::load(name, opts) {
50-
Ok(f) => match f.get_boxed() {
51-
Ok(v) => self.append_boxed(v),
52-
Err(e) => {
53-
error!("failed to append filter '{}': {:?}", name, e);
54-
self
55-
}
56-
},
57-
Err(e) => {
58-
error!("failed to append filter '{}': {:?}", name, e);
59-
self
60-
}
61-
}
41+
self.append_boxed(Box::new(next));
42+
self
6243
}
6344

64-
pub(crate) fn append_boxed(mut self, next: Box<dyn Filter>) -> Self {
45+
pub(crate) fn append_boxed(&mut self, next: Box<dyn Filter>) {
6546
self.filters.push_back(next);
66-
self
6747
}
6848

6949
pub(crate) fn build(self) -> Option<FilteredHandler> {

0 commit comments

Comments
 (0)