Skip to content

Commit

Permalink
feat: implement lua filter
Browse files Browse the repository at this point in the history
  • Loading branch information
jjeffcaii committed Oct 8, 2024
1 parent 70dcf87 commit ebc81ca
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 147 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zerodns"
version = "0.1.0-alpha.4"
version = "0.1.0-alpha.5"
edition = "2021"
license = "MIT"
readme = "README.md"
Expand Down
38 changes: 31 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

a DNS server in Rust, which is inspired from chinadns/dnsmasq.

> WARNING: still in an active development!!!
> :construction_worker: WARNING: still in an active development!!!
## Goals

Expand Down Expand Up @@ -62,6 +62,31 @@ props = { servers = ["tcp://208.67.222.222:443", "tcp://208.67.220.220:443"] }
kind = "chinadns"
props = { trusted = ["tcp://208.67.222.222:443", "tcp://208.67.220.220:443"], mistrusted = ["223.5.5.5", "223.6.6.6"], geoip_database = "GeoLite2-Country.mmdb" }

# a lua filter example which show how to resolve addr by lua, see src/filter/lua.rs for more infomation.
[filters.lua]
kind = "lua"
props.script = """
-- The filter entrance:
function handle(ctx)
-- log something...
for i,v in ipairs(ctx.request:questions()) do
logger:info('---- question#'..i..': '..v.name)
end
-- resolve addr from 223.5.5.5
local resp = resolve(ctx.request,'223.5.5.5')
-- log something...
for i,v in ipairs(resp:answers()) do
logger:info('---- answers#'..i..': name='..v.name..', rdata='..v.rdata)
end
-- answer it!
ctx:answer(resp)
end
"""

##### FILTERS END #####

##### RULES BEGIN #####
Expand All @@ -70,27 +95,26 @@ props = { trusted = ["tcp://208.67.222.222:443", "tcp://208.67.220.220:443"], mi
# - will check rules below one by one
# - the 'domain' field follows the glob syntax

# for domain of '*.cn', use alidns filter
# RULE-1: for those domains of '*.cn', use lua filter
[[rules]]
domain = "*.cn"
filter = "alidns"
filters = ["lua"]

# for domain of '*apple.com', use alidns filter
# RULE-2: for those domains of '*apple.com', use alidns filter
[[rules]]
domain = "*.apple.com"
filter = "alidns"

# for domain of '*google*', use opendns filter
# RULE-3: for those domains of '*google*', use opendns filter
[[rules]]
domain = "*google*"
filter = "opendns"

# FINAL: use chinadns for others
# RULE-FINAL: use chinadns for others
[[rules]]
domain = "*"
filter = "chinadns"

##### RULES END #####


```
32 changes: 28 additions & 4 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,42 @@ props.trusted = ["tcp://208.67.222.222:443", "tcp://208.67.220.220:443"]
props.mistrusted = ["223.5.5.5", "223.6.6.6"]
props.geoip_database = "GeoLite2-Country.mmdb"

[filters.lua]
kind = "lua"
props.script = """
-- The filter entrance:
function handle(ctx)
-- log something...
for i,v in ipairs(ctx.request:questions()) do
logger:info('---- question#'..i..': '..v.name)
end
-- resolve addr from 223.5.5.5
local resp = resolve(ctx.request,'223.5.5.5')
-- log something...
for i,v in ipairs(resp:answers()) do
logger:info('---- answers#'..i..': name='..v.name..', rdata='..v.rdata)
end
-- answer it!
ctx:answer(resp)
end
"""

[[rules]]
domain = "*.cn"
filter = "alidns"
filters = ["lua"]

[[rules]]
domain = "*.apple.com"
filter = "alidns"
filters = ["alidns"]

[[rules]]
domain = "*google*"
filter = "opendns"
filters = ["opendns"]

[[rules]]
domain = "*"
filter = "chinadns"
filters = ["chinadns"]
33 changes: 33 additions & 0 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,36 @@ pub async fn request(dns: &DNS, request: &Message) -> Result<Message> {

ret
}

#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;

fn init() {
pretty_env_logger::try_init_timed().ok();
}

#[tokio::test]
async fn test_request() -> anyhow::Result<()> {
init();

let dns = DNS::from_str("223.5.5.5")?;
let req = {
// type=A domain=baidu.com
let raw = hex::decode(
"128e0120000100000000000105626169647503636f6d00000100010000291000000000000000",
)?;
Message::from(raw)
};

let res = request(&dns, &req).await;

assert!(res.is_ok_and(|msg| {
info!("message: {:?}", msg);
true
}));

Ok(())
}
}
4 changes: 3 additions & 1 deletion src/client/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ mod tests {
async fn test_request() -> Result<()> {
init();

let c = TcpClient::builder("199.85.127.10:53".parse()?).build()?;
let c = TcpClient::builder("1.1.1.1:53".parse()?)
.timeout(Duration::from_secs(5))
.build()?;

for _ in 0..3 {
let req = Message::builder()
Expand Down
12 changes: 8 additions & 4 deletions src/client/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ impl UdpClient {

let mut framed = UdpFramed::new(socket, BytesCodec::default());

framed.send((req.clone().0.freeze(), self.addr)).await?;
let bb = req.clone().0.freeze();

if let Err(e) = framed.send((bb, self.addr)).await {
return Err(e.into());
}

match framed.next().await {
Some(next) => {
Expand Down Expand Up @@ -74,7 +78,7 @@ mod tests {
use crate::client::Client;
use crate::protocol::{Class, Flags, Kind, Message, OpCode};

use super::UdpClient;
use super::*;

fn init() {
pretty_env_logger::try_init_timed().ok();
Expand All @@ -84,8 +88,8 @@ mod tests {
async fn test_request() -> anyhow::Result<()> {
init();

let c = UdpClient::builder("199.85.127.10:53".parse()?)
.timeout(Duration::from_secs(3))
let c = UdpClient::builder("1.1.1.1:53".parse()?)
.timeout(Duration::from_secs(5))
.build();

let req = Message::builder()
Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct Filter {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rule {
pub domain: String,
pub filter: String,
pub filters: Vec<String>,
}

pub fn read_from_toml(pt: PathBuf) -> anyhow::Result<Config> {
Expand Down
94 changes: 48 additions & 46 deletions src/filter/chinadns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use crate::client::request;
use async_trait::async_trait;
use maxminddb::Reader;

use tokio::sync::mpsc;

use crate::filter::misc::OptionsReader;
use crate::protocol::{Kind, Message, RData, DNS};
use crate::Result;

use super::{Context, Filter, FilterFactory, Options};
use super::{handle_next, Context, Filter, FilterFactory, Options};

pub(crate) struct ChinaDNSFilter {
trusted: Arc<Vec<DNS>>,
Expand Down Expand Up @@ -70,55 +72,55 @@ impl Filter for ChinaDNSFilter {
req: &mut Message,
res: &mut Option<Message>,
) -> Result<()> {
debug!("filter on chinadns");

let (tx, mut rx) = tokio::sync::mpsc::channel::<(bool, Message)>(1);

let mistrusted = Clone::clone(&self.mistrusted);
{
let msg = Clone::clone(req);
let geoip = Clone::clone(&self.geoip);
let tx = Clone::clone(&tx);
tokio::spawn(async move {
if let Some(msg) = Self::request(&msg, &mistrusted, &geoip, true).await {
tx.send((true, msg)).await.ok();
}
});
}
if res.is_none() {
let (tx, mut rx) = mpsc::channel::<(bool, Message)>(1);

let trusted = Clone::clone(&self.trusted);
let mistrusted = Clone::clone(&self.mistrusted);

// resolve from mistrusted dns
{
let msg = Clone::clone(req);
let geoip = Clone::clone(&self.geoip);
let tx = Clone::clone(&tx);
tokio::spawn(async move {
if let Some(msg) = Self::request(&msg, &mistrusted, &geoip, true).await {
tx.send((true, msg)).await.ok();
}
});
}

let trusted = Clone::clone(&self.trusted);
{
let msg = Clone::clone(req);
let geoip = Clone::clone(&self.geoip);
tokio::spawn(async move {
if let Some(msg) = Self::request(&msg, &trusted, &geoip, false).await {
tx.send((false, msg)).await.ok();
}
});
}
// resolve from trusted dns
{
let msg = Clone::clone(req);
let geoip = Clone::clone(&self.geoip);
tokio::spawn(async move {
if let Some(msg) = Self::request(&msg, &trusted, &geoip, false).await {
tx.send((false, msg)).await.ok();
}
});
}

// let mut domain = SmallVec::<[u8; 64]>::new();
// for (i, b) in req.questions().next().unwrap().name().enumerate() {
// if i != 0 {
// domain.push(b'.');
// }
// domain.extend_from_slice(b);
// }

if let Some((china, msg)) = rx.recv().await {
// info!(
// "{}: oversea={}",
// String::from_utf8_lossy(&domain[..]),
// !china
// );

res.replace(msg);
// let mut domain = SmallVec::<[u8; 64]>::new();
// for (i, b) in req.questions().next().unwrap().name().enumerate() {
// if i != 0 {
// domain.push(b'.');
// }
// domain.extend_from_slice(b);
// }

if let Some((china, msg)) = rx.recv().await {
// info!(
// "{}: oversea={}",
// String::from_utf8_lossy(&domain[..]),
// !china
// );

res.replace(msg);
}
}

match &self.next {
Some(next) => next.handle(ctx, req, res).await,
None => Ok(()),
}
handle_next(self.next.as_deref(), ctx, req, res).await
}

fn set_next(&mut self, next: Box<dyn Filter>) {
Expand Down
Loading

0 comments on commit ebc81ca

Please sign in to comment.